Kaynağa Gözat

Add swd_probe from https://github.com/xMasterX/all-the-plugins

git-subtree-dir: swd_probe
git-subtree-mainline: 042fc95bd2a3c8db9a7a5ac5e6263cee94ebb1ad
git-subtree-split: 0c316d85586678a264aa155b81c8075c80da7ae2
Willy-JL 2 yıl önce
ebeveyn
işleme
63f196c15f

+ 52 - 0
swd_probe/.gitignore

@@ -0,0 +1,52 @@
+# Prerequisites
+*.d
+
+# Object files
+*.o
+*.ko
+*.obj
+*.elf
+
+# Linker output
+*.ilk
+*.map
+*.exp
+
+# Precompiled Headers
+*.gch
+*.pch
+
+# Libraries
+*.lib
+*.a
+*.la
+*.lo
+
+# Shared objects (inc. Windows DLLs)
+*.dll
+*.so
+*.so.*
+*.dylib
+
+# Executables
+*.exe
+*.out
+*.app
+*.i*86
+*.x86_64
+*.hex
+
+# Debug files
+*.dSYM/
+*.su
+*.idb
+*.pdb
+
+# Kernel Module Compile Results
+*.mod*
+*.cmd
+.tmp_versions/
+modules.order
+Module.symvers
+Mkfile.old
+dkms.conf

+ 1 - 0
swd_probe/.gitsubtree

@@ -0,0 +1 @@
+https://github.com/xMasterX/all-the-plugins dev base_pack/swd_probe

+ 674 - 0
swd_probe/LICENSE.txt

@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+them if you wish), that you receive source code or can get it if you
+want it, that you can change the software or use pieces of it in new
+free programs, and that you know you can do these things.
+
+  To protect your rights, we need to prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  You must make sure that they, too, receive
+or can get the source code.  And you must show them these terms so they
+know their rights.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey verbatim copies of the Program's source code as you
+receive it, in any medium, provided that you conspicuously and
+appropriately publish on each copy an appropriate copyright notice;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+  Each version is given a distinguishing version number.  If the
+Program specifies that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
+APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
+HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
+OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
+THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
+IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
+ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
+GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
+USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
+DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
+PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
+EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
+SUCH DAMAGES.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     END OF TERMS AND CONDITIONS
+
+            How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software: you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation, either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU General Public License does not permit incorporating your program
+into proprietary programs.  If your program is a subroutine library, you
+may consider it more useful to permit linking proprietary applications with
+the library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.

+ 17 - 0
swd_probe/README.md

@@ -0,0 +1,17 @@
+# ARM SWD (Single Wire Debug) Probe
+
+Modern microcontrollers have support for the two wire debug interface SWD, which makes wiring a lot simpler.
+When reverse engineering, finding these two pins is a los easier than with JTAG, where you had to wire up twice or more pins. However, finding  the two pins is still a bit of work, which gets simplified even more with this application.
+
+This application tries to detect a valid SWD response on the wires you have picked and beeps when you have found the correct pins, showing the detected ID register and, more important, the SWD pinout. It doesn't matter which two pins you choose, just pick any two from the GPIOs on the breakout header. 
+
+To achieve this, the application sends packets and scans the response on all pins and elaborates the pins within a few retries. Using some kind of bisect pattern reduces this number to a hand full of tries, yielding in a seemingly instant detection. 
+
+For the user it is as simple as a continuity tester - wire up your two test needles (or accupuncture needles), connect the obvious GND pin and probe all test pads.
+Now it depends on your bisect capabilities finding all pad combinations, how long it will take this time.
+
+https://cdn.discordapp.com/attachments/954430078882816021/1071603366741938176/20230205_022641.mp4
+
+https://cdn.discordapp.com/attachments/1071712925171056690/1072306469057347594/qFlipper_2023-02-07_01-01-24.mp4 
+
+Discussion thread: https://discord.com/channels/740930220399525928/1071712925171056690

+ 1016 - 0
swd_probe/adi.c

@@ -0,0 +1,1016 @@
+
+#include <furi.h>
+#include <stdlib.h>
+
+#include "adi.h"
+#include "swd_probe_app.h"
+
+/* https://github.com/openocd-org/openocd/blob/master/src/target/arm_adi_v5.c */
+
+/*
+static const char* class_description[16] = {
+    [0x0] = "Generic verification component",
+    [0x1] = "(ROM Table)",
+    [0x2] = "Reserved",
+    [0x3] = "Reserved",
+    [0x4] = "Reserved",
+    [0x5] = "Reserved",
+    [0x6] = "Reserved",
+    [0x7] = "Reserved",
+    [0x8] = "Reserved",
+    [0x9] = "CoreSight component",
+    [0xA] = "Reserved",
+    [0xB] = "Peripheral Test Block",
+    [0xC] = "Reserved",
+    [0xD] = "OptimoDE DESS",
+    [0xE] = "Generic IP component",
+    [0xF] = "CoreLink, PrimeCell or System component",
+};
+*/
+
+static const struct {
+    uint32_t arch_id;
+    const char* description;
+} class0x9_devarch[] = {
+    /* keep same unsorted order as in ARM IHI0029E */
+    {ARCH_ID(ARM_ID, 0x0A00), "RAS architecture"},
+    {ARCH_ID(ARM_ID, 0x1A01), "Instrumentation Trace Macrocell (ITM) architecture"},
+    {ARCH_ID(ARM_ID, 0x1A02), "DWT architecture"},
+    {ARCH_ID(ARM_ID, 0x1A03), "Flash Patch and Breakpoint unit (FPB) architecture"},
+    {ARCH_ID(ARM_ID, 0x2A04), "Processor debug architecture (ARMv8-M)"},
+    {ARCH_ID(ARM_ID, 0x6A05), "Processor debug architecture (ARMv8-R)"},
+    {ARCH_ID(ARM_ID, 0x0A10), "PC sample-based profiling"},
+    {ARCH_ID(ARM_ID, 0x4A13), "Embedded Trace Macrocell (ETM) architecture"},
+    {ARCH_ID(ARM_ID, 0x1A14), "Cross Trigger Interface (CTI) architecture"},
+    {ARCH_ID(ARM_ID, 0x6A15), "Processor debug architecture (v8.0-A)"},
+    {ARCH_ID(ARM_ID, 0x7A15), "Processor debug architecture (v8.1-A)"},
+    {ARCH_ID(ARM_ID, 0x8A15), "Processor debug architecture (v8.2-A)"},
+    {ARCH_ID(ARM_ID, 0x2A16), "Processor Performance Monitor (PMU) architecture"},
+    {ARCH_ID(ARM_ID, 0x0A17), "Memory Access Port v2 architecture"},
+    {ARCH_ID(ARM_ID, 0x0A27), "JTAG Access Port v2 architecture"},
+    {ARCH_ID(ARM_ID, 0x0A31), "Basic trace router"},
+    {ARCH_ID(ARM_ID, 0x0A37), "Power requestor"},
+    {ARCH_ID(ARM_ID, 0x0A47), "Unknown Access Port v2 architecture"},
+    {ARCH_ID(ARM_ID, 0x0A50), "HSSTP architecture"},
+    {ARCH_ID(ARM_ID, 0x0A63), "System Trace Macrocell (STM) architecture"},
+    {ARCH_ID(ARM_ID, 0x0A75), "CoreSight ELA architecture"},
+    {ARCH_ID(ARM_ID, 0x0AF7), "CoreSight ROM architecture"},
+};
+
+/* Part number interpretations are from Cortex
+ * core specs, the CoreSight components TRM
+ * (ARM DDI 0314H), CoreSight System Design
+ * Guide (ARM DGI 0012D) and ETM specs; also
+ * from chip observation (e.g. TI SDTI).
+ */
+
+static const struct dap_part_nums {
+    uint16_t designer_id;
+    uint16_t part_num;
+    const char* type;
+    const char* full;
+} dap_part_nums[] = {
+    {
+        ARM_ID,
+        0x000,
+        "Cortex-M3 SCS",
+        "(System Control Space)",
+    },
+    {
+        ARM_ID,
+        0x001,
+        "Cortex-M3 ITM",
+        "(Instrumentation Trace Module)",
+    },
+    {
+        ARM_ID,
+        0x002,
+        "Cortex-M3 DWT",
+        "(Data Watchpoint and Trace)",
+    },
+    {
+        ARM_ID,
+        0x003,
+        "Cortex-M3 FPB",
+        "(Flash Patch and Breakpoint)",
+    },
+    {
+        ARM_ID,
+        0x008,
+        "Cortex-M0 SCS",
+        "(System Control Space)",
+    },
+    {
+        ARM_ID,
+        0x00a,
+        "Cortex-M0 DWT",
+        "(Data Watchpoint and Trace)",
+    },
+    {
+        ARM_ID,
+        0x00b,
+        "Cortex-M0 BPU",
+        "(Breakpoint Unit)",
+    },
+    {
+        ARM_ID,
+        0x00c,
+        "Cortex-M4 SCS",
+        "(System Control Space)",
+    },
+    {
+        ARM_ID,
+        0x00d,
+        "CoreSight ETM11",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x00e,
+        "Cortex-M7 FPB",
+        "(Flash Patch and Breakpoint)",
+    },
+    {
+        ARM_ID,
+        0x193,
+        "SoC-600 TSGEN",
+        "(Timestamp Generator)",
+    },
+    {
+        ARM_ID,
+        0x470,
+        "Cortex-M1 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x471,
+        "Cortex-M0 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x490,
+        "Cortex-A15 GIC",
+        "(Generic Interrupt Controller)",
+    },
+    {
+        ARM_ID,
+        0x492,
+        "Cortex-R52 GICD",
+        "(Distributor)",
+    },
+    {
+        ARM_ID,
+        0x493,
+        "Cortex-R52 GICR",
+        "(Redistributor)",
+    },
+    {
+        ARM_ID,
+        0x4a1,
+        "Cortex-A53 ROM",
+        "(v8 Memory Map ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4a2,
+        "Cortex-A57 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4a3,
+        "Cortex-A53 ROM",
+        "(v7 Memory Map ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4a4,
+        "Cortex-A72 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4a9,
+        "Cortex-A9 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4aa,
+        "Cortex-A35 ROM",
+        "(v8 Memory Map ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4af,
+        "Cortex-A15 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4b5,
+        "Cortex-R5 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4b8,
+        "Cortex-R52 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4c0,
+        "Cortex-M0+ ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4c3,
+        "Cortex-M3 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4c4,
+        "Cortex-M4 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4c7,
+        "Cortex-M7 PPB ROM",
+        "(Private Peripheral Bus ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4c8,
+        "Cortex-M7 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4e0,
+        "Cortex-A35 ROM",
+        "(v7 Memory Map ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x4e4,
+        "Cortex-A76 ROM",
+        "(ROM Table)",
+    },
+    {
+        ARM_ID,
+        0x906,
+        "CoreSight CTI",
+        "(Cross Trigger)",
+    },
+    {
+        ARM_ID,
+        0x907,
+        "CoreSight ETB",
+        "(Trace Buffer)",
+    },
+    {
+        ARM_ID,
+        0x908,
+        "CoreSight CSTF",
+        "(Trace Funnel)",
+    },
+    {
+        ARM_ID,
+        0x909,
+        "CoreSight ATBR",
+        "(Advanced Trace Bus Replicator)",
+    },
+    {
+        ARM_ID,
+        0x910,
+        "CoreSight ETM9",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x912,
+        "CoreSight TPIU",
+        "(Trace Port Interface Unit)",
+    },
+    {
+        ARM_ID,
+        0x913,
+        "CoreSight ITM",
+        "(Instrumentation Trace Macrocell)",
+    },
+    {
+        ARM_ID,
+        0x914,
+        "CoreSight SWO",
+        "(Single Wire Output)",
+    },
+    {
+        ARM_ID,
+        0x917,
+        "CoreSight HTM",
+        "(AHB Trace Macrocell)",
+    },
+    {
+        ARM_ID,
+        0x920,
+        "CoreSight ETM11",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x921,
+        "Cortex-A8 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x922,
+        "Cortex-A8 CTI",
+        "(Cross Trigger)",
+    },
+    {
+        ARM_ID,
+        0x923,
+        "Cortex-M3 TPIU",
+        "(Trace Port Interface Unit)",
+    },
+    {
+        ARM_ID,
+        0x924,
+        "Cortex-M3 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x925,
+        "Cortex-M4 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x930,
+        "Cortex-R4 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x931,
+        "Cortex-R5 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x932,
+        "CoreSight MTB-M0+",
+        "(Micro Trace Buffer)",
+    },
+    {
+        ARM_ID,
+        0x941,
+        "CoreSight TPIU-Lite",
+        "(Trace Port Interface Unit)",
+    },
+    {
+        ARM_ID,
+        0x950,
+        "Cortex-A9 PTM",
+        "(Program Trace Macrocell)",
+    },
+    {
+        ARM_ID,
+        0x955,
+        "Cortex-A5 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x95a,
+        "Cortex-A72 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x95b,
+        "Cortex-A17 PTM",
+        "(Program Trace Macrocell)",
+    },
+    {
+        ARM_ID,
+        0x95d,
+        "Cortex-A53 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x95e,
+        "Cortex-A57 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x95f,
+        "Cortex-A15 PTM",
+        "(Program Trace Macrocell)",
+    },
+    {
+        ARM_ID,
+        0x961,
+        "CoreSight TMC",
+        "(Trace Memory Controller)",
+    },
+    {
+        ARM_ID,
+        0x962,
+        "CoreSight STM",
+        "(System Trace Macrocell)",
+    },
+    {
+        ARM_ID,
+        0x975,
+        "Cortex-M7 ETM",
+        "(Embedded Trace)",
+    },
+    {
+        ARM_ID,
+        0x9a0,
+        "CoreSight PMU",
+        "(Performance Monitoring Unit)",
+    },
+    {
+        ARM_ID,
+        0x9a1,
+        "Cortex-M4 TPIU",
+        "(Trace Port Interface Unit)",
+    },
+    {
+        ARM_ID,
+        0x9a4,
+        "CoreSight GPR",
+        "(Granular Power Requester)",
+    },
+    {
+        ARM_ID,
+        0x9a5,
+        "Cortex-A5 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9a7,
+        "Cortex-A7 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9a8,
+        "Cortex-A53 CTI",
+        "(Cross Trigger)",
+    },
+    {
+        ARM_ID,
+        0x9a9,
+        "Cortex-M7 TPIU",
+        "(Trace Port Interface Unit)",
+    },
+    {
+        ARM_ID,
+        0x9ae,
+        "Cortex-A17 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9af,
+        "Cortex-A15 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9b6,
+        "Cortex-R52 PMU/CTI/ETM",
+        "(Performance Monitor Unit/Cross Trigger/ETM)",
+    },
+    {
+        ARM_ID,
+        0x9b7,
+        "Cortex-R7 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9d3,
+        "Cortex-A53 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9d7,
+        "Cortex-A57 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9d8,
+        "Cortex-A72 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        ARM_ID,
+        0x9da,
+        "Cortex-A35 PMU/CTI/ETM",
+        "(Performance Monitor Unit/Cross Trigger/ETM)",
+    },
+    {
+        ARM_ID,
+        0x9e2,
+        "SoC-600 APB-AP",
+        "(APB4 Memory Access Port)",
+    },
+    {
+        ARM_ID,
+        0x9e3,
+        "SoC-600 AHB-AP",
+        "(AHB5 Memory Access Port)",
+    },
+    {
+        ARM_ID,
+        0x9e4,
+        "SoC-600 AXI-AP",
+        "(AXI Memory Access Port)",
+    },
+    {
+        ARM_ID,
+        0x9e5,
+        "SoC-600 APv1 Adapter",
+        "(Access Port v1 Adapter)",
+    },
+    {
+        ARM_ID,
+        0x9e6,
+        "SoC-600 JTAG-AP",
+        "(JTAG Access Port)",
+    },
+    {
+        ARM_ID,
+        0x9e7,
+        "SoC-600 TPIU",
+        "(Trace Port Interface Unit)",
+    },
+    {
+        ARM_ID,
+        0x9e8,
+        "SoC-600 TMC ETR/ETS",
+        "(Embedded Trace Router/Streamer)",
+    },
+    {
+        ARM_ID,
+        0x9e9,
+        "SoC-600 TMC ETB",
+        "(Embedded Trace Buffer)",
+    },
+    {
+        ARM_ID,
+        0x9ea,
+        "SoC-600 TMC ETF",
+        "(Embedded Trace FIFO)",
+    },
+    {
+        ARM_ID,
+        0x9eb,
+        "SoC-600 ATB Funnel",
+        "(Trace Funnel)",
+    },
+    {
+        ARM_ID,
+        0x9ec,
+        "SoC-600 ATB Replicator",
+        "(Trace Replicator)",
+    },
+    {
+        ARM_ID,
+        0x9ed,
+        "SoC-600 CTI",
+        "(Cross Trigger)",
+    },
+    {
+        ARM_ID,
+        0x9ee,
+        "SoC-600 CATU",
+        "(Address Translation Unit)",
+    },
+    {
+        ARM_ID,
+        0xc05,
+        "Cortex-A5 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc07,
+        "Cortex-A7 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc08,
+        "Cortex-A8 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc09,
+        "Cortex-A9 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc0e,
+        "Cortex-A17 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc0f,
+        "Cortex-A15 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc14,
+        "Cortex-R4 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc15,
+        "Cortex-R5 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xc17,
+        "Cortex-R7 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd03,
+        "Cortex-A53 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd04,
+        "Cortex-A35 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd07,
+        "Cortex-A57 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd08,
+        "Cortex-A72 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd0b,
+        "Cortex-A76 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd0c,
+        "Neoverse N1",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd13,
+        "Cortex-R52 Debug",
+        "(Debug Unit)",
+    },
+    {
+        ARM_ID,
+        0xd49,
+        "Neoverse N2",
+        "(Debug Unit)",
+    },
+    {
+        0x017,
+        0x120,
+        "TI SDTI",
+        "(System Debug Trace Interface)",
+    }, /* from OMAP3 memmap */
+    {
+        0x017,
+        0x343,
+        "TI DAPCTL",
+        "",
+    }, /* from OMAP3 memmap */
+    {0x017, 0x9af, "MSP432 ROM", "(ROM Table)"},
+    {0x01f, 0xcd0, "Atmel CPU with DSU", "(CPU)"},
+    {0x041, 0x1db, "XMC4500 ROM", "(ROM Table)"},
+    {0x041, 0x1df, "XMC4700/4800 ROM", "(ROM Table)"},
+    {0x041, 0x1ed, "XMC1000 ROM", "(ROM Table)"},
+    {
+        0x065,
+        0x000,
+        "SHARC+/Blackfin+",
+        "",
+    },
+    {
+        0x070,
+        0x440,
+        "Qualcomm QDSS Component v1",
+        "(Qualcomm Designed CoreSight Component v1)",
+    },
+    {
+        0x0bf,
+        0x100,
+        "Brahma-B53 Debug",
+        "(Debug Unit)",
+    },
+    {
+        0x0bf,
+        0x9d3,
+        "Brahma-B53 PMU",
+        "(Performance Monitor Unit)",
+    },
+    {
+        0x0bf,
+        0x4a1,
+        "Brahma-B53 ROM",
+        "(ROM Table)",
+    },
+    {
+        0x0bf,
+        0x721,
+        "Brahma-B53 ROM",
+        "(ROM Table)",
+    },
+    {
+        0x1eb,
+        0x181,
+        "Tegra 186 ROM",
+        "(ROM Table)",
+    },
+    {
+        0x1eb,
+        0x202,
+        "Denver ETM",
+        "(Denver Embedded Trace)",
+    },
+    {
+        0x1eb,
+        0x211,
+        "Tegra 210 ROM",
+        "(ROM Table)",
+    },
+    {
+        0x1eb,
+        0x302,
+        "Denver Debug",
+        "(Debug Unit)",
+    },
+    {
+        0x1eb,
+        0x402,
+        "Denver PMU",
+        "(Performance Monitor Unit)",
+    },
+    {0x20, 0x410, "STM32F10 (med)", "(ROM Table)"},
+    {0x20, 0x411, "STM32F2", "(ROM Table)"},
+    {0x20, 0x412, "STM32F10 (low)", "(ROM Table)"},
+    {0x20, 0x413, "STM32F40/41", "(ROM Table)"},
+    {0x20, 0x414, "STM32F10 (high)", "(ROM Table)"},
+    {0x20, 0x415, "STM32L47/48", "(ROM Table)"},
+    {0x20, 0x416, "STM32L1xxx6/8/B", "(ROM Table)"},
+    {0x20, 0x417, "STM32L05/06", "(ROM Table)"},
+    {0x20, 0x418, "STM32F105xx/107", "(ROM Table)"},
+    {0x20, 0x419, "STM32F42/43", "(ROM Table)"},
+    {0x20, 0x420, "STM32F10 (med)", "(ROM Table)"},
+    {0x20, 0x421, "STM32F446xx", "(ROM Table)"},
+    {0x20, 0x422, "STM32FF358/02/03", "(ROM Table)"},
+    {0x20, 0x423, "STM32F401xB/C", "(ROM Table)"},
+    {0x20, 0x425, "STM32L031/41", "(ROM Table)"},
+    {0x20, 0x427, "STM32L1xxxC", "(ROM Table)"},
+    {0x20, 0x428, "STM32F10 (high)", "(ROM Table)"},
+    {0x20, 0x429, "STM32L1xxx6A/8A/BA", "(ROM Table)"},
+    {0x20, 0x430, "STM32F10 (xl)", "(ROM Table)"},
+    {0x20, 0x431, "STM32F411xx", "(ROM Table)"},
+    {0x20, 0x432, "STM32F373/8", "(ROM Table)"},
+    {0x20, 0x433, "STM32F401xD/E", "(ROM Table)"},
+    {0x20, 0x434, "STM32F469/79", "(ROM Table)"},
+    {0x20, 0x435, "STM32L43/44", "(ROM Table)"},
+    {0x20, 0x436, "STM32L1xxxD", "(ROM Table)"},
+    {0x20, 0x437, "STM32L1xxxE", "(ROM Table)"},
+    {0x20, 0x438, "STM32F303/34/28", "(ROM Table)"},
+    {0x20, 0x439, "STM32F301/02/18	", "(ROM Table)"},
+    {0x20, 0x440, "STM32F03/5", "(ROM Table)"},
+    {0x20, 0x441, "STM32F412xx", "(ROM Table)"},
+    {0x20, 0x442, "STM32F03/9", "(ROM Table)"},
+    {0x20, 0x444, "STM32F03xx4", "(ROM Table)"},
+    {0x20, 0x445, "STM32F04/7", "(ROM Table)"},
+    {0x20, 0x446, "STM32F302/03/98", "(ROM Table)"},
+    {0x20, 0x447, "STM32L07/08", "(ROM Table)"},
+    {0x20, 0x448, "STM32F070/1/2", "(ROM Table)"},
+    {0x20, 0x449, "STM32F74/5", "(ROM Table)"},
+    {0x20, 0x450, "STM32H74/5", "(ROM Table)"},
+    {0x20, 0x451, "STM32F76/7", "(ROM Table)"},
+    {0x20, 0x452, "STM32F72/3", "(ROM Table)"},
+    {0x20, 0x457, "STM32L01/2", "(ROM Table)"},
+    {0x20, 0x458, "STM32F410xx", "(ROM Table)"},
+    {0x20, 0x460, "STM32G07/8", "(ROM Table)"},
+    {0x20, 0x461, "STM32L496/A6", "(ROM Table)"},
+    {0x20, 0x462, "STM32L45/46", "(ROM Table)"},
+    {0x20, 0x463, "STM32F413/23", "(ROM Table)"},
+    {0x20, 0x464, "STM32L412/22", "(ROM Table)"},
+    {0x20, 0x466, "STM32G03/04", "(ROM Table)"},
+    {0x20, 0x468, "STM32G431/41", "(ROM Table)"},
+    {0x20, 0x469, "STM32G47/48", "(ROM Table)"},
+    {0x20, 0x470, "STM32L4R/S", "(ROM Table)"},
+    {0x20, 0x471, "STM32L4P5/Q5", "(ROM Table)"},
+    {0x20, 0x479, "STM32G491xx", "(ROM Table)"},
+    {0x20, 0x480, "STM32H7A/B", "(ROM Table)"},
+    {0x20, 0x495, "STM32WB50/55", "(ROM Table)"},
+    {0x20, 0x497, "STM32WLE5xx", "(ROM Table)"}};
+
+const char* adi_devarch_desc(uint32_t devarch) {
+    if(!(devarch & ARM_CS_C9_DEVARCH_PRESENT)) {
+        return "not present";
+    }
+
+    for(unsigned int i = 0; i < ARRAY_SIZE(class0x9_devarch); i++) {
+        if((devarch & DEVARCH_ID_MASK) == class0x9_devarch[i].arch_id) {
+            return class0x9_devarch[i].description;
+        }
+    }
+
+    return "unknown";
+}
+
+const struct dap_part_nums* adi_part_num(unsigned int des, unsigned int part) {
+    static char buf[32];
+    static struct dap_part_nums unknown = {
+        .type = "Unrecognized",
+        .full = "",
+    };
+
+    for(unsigned int i = 0; i < ARRAY_SIZE(dap_part_nums); i++) {
+        if(dap_part_nums[i].designer_id == des && dap_part_nums[i].part_num == part) {
+            return &dap_part_nums[i];
+        }
+    }
+
+    snprintf(buf, sizeof(buf), "D:%x P:%x", des, part);
+    unknown.full = buf;
+
+    return &unknown;
+}
+
+bool adi_get_pidr(AppFSM* const ctx, uint32_t base, pidr_data_t* data) {
+    uint32_t pidrs[7];
+    uint32_t offsets[] = {0xFE0, 0xFE4, 0xFE8, 0xFEC, 0xFD0, 0xFD4, 0xFD8, 0xFDC};
+
+    furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+    for(size_t pos = 0; pos < COUNT(pidrs); pos++) {
+        uint8_t ret = swd_read_memory(ctx, ctx->ap_pos, base + offsets[pos], &pidrs[pos]);
+        if(ret != 1) {
+            DBGS("Read failed");
+            furi_mutex_release(ctx->swd_mutex);
+            return false;
+        }
+    }
+    furi_mutex_release(ctx->swd_mutex);
+
+    data->designer = ((pidrs[4] & 0x0F) << 7) | ((pidrs[2] & 0x07) << 4) |
+                     ((pidrs[1] >> 4) & 0x0F);
+    data->part = (pidrs[0] & 0xFF) | ((pidrs[1] & 0x0F) << 8);
+    data->revand = ((pidrs[3] >> 4) & 0x0F);
+    data->cmod = (pidrs[3] & 0x0F);
+    data->revision = ((pidrs[2] >> 4) & 0x0F);
+    data->size = ((pidrs[2] >> 4) & 0x0F);
+
+    return true;
+}
+
+bool adi_get_class(AppFSM* const ctx, uint32_t base, uint8_t* class) {
+    uint32_t cidrs[4];
+    uint32_t offsets[] = {0xFF0, 0xFF4, 0xFF8, 0xFFC};
+
+    furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+    for(size_t pos = 0; pos < COUNT(cidrs); pos++) {
+        uint8_t ret = swd_read_memory(ctx, ctx->ap_pos, base + offsets[pos], &cidrs[pos]);
+        if(ret != 1) {
+            DBGS("Read failed");
+            furi_mutex_release(ctx->swd_mutex);
+            return false;
+        }
+    }
+    furi_mutex_release(ctx->swd_mutex);
+
+    if((cidrs[0] & 0xFF) != 0x0D) {
+        return false;
+    }
+    if((cidrs[1] & 0x0F) != 0x00) {
+        return false;
+    }
+    if((cidrs[2] & 0xFF) != 0x05) {
+        return false;
+    }
+    if((cidrs[3] & 0xFF) != 0xB1) {
+        return false;
+    }
+
+    *class = ((cidrs[1] >> 4) & 0x0F);
+
+    return true;
+}
+
+const char* adi_romtable_type(AppFSM* const ctx, uint32_t base) {
+    pidr_data_t data;
+
+    if(!adi_get_pidr(ctx, base, &data)) {
+        return "fail";
+    }
+    const struct dap_part_nums* info = adi_part_num(data.designer, data.part);
+
+    return info->type;
+}
+
+const char* adi_romtable_full(AppFSM* const ctx, uint32_t base) {
+    pidr_data_t data;
+
+    if(!adi_get_pidr(ctx, base, &data)) {
+        return "fail";
+    }
+    const struct dap_part_nums* info = adi_part_num(data.designer, data.part);
+
+    return info->full;
+}
+
+uint32_t adi_romtable_entry_count(AppFSM* const ctx, uint32_t base) {
+    uint32_t count = 0;
+    uint32_t entry = 0;
+
+    furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+    for(size_t pos = 0; pos < 960; pos++) {
+        uint8_t ret = 0;
+        for(int tries = 0; tries < 10 && ret != 1; tries++) {
+            ret = swd_read_memory(ctx, ctx->ap_pos, base + pos * 4, &entry);
+        }
+        if(ret != 1) {
+            DBGS("Read failed");
+            break;
+        }
+        if(!(entry & 1)) {
+            break;
+        }
+        if(entry & 0x00000FFC) {
+            break;
+        }
+        count++;
+    }
+    furi_mutex_release(ctx->swd_mutex);
+    return count;
+}
+
+uint32_t adi_romtable_get(AppFSM* const ctx, uint32_t base, uint32_t pos) {
+    uint32_t entry = 0;
+
+    furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+    uint8_t ret = swd_read_memory(ctx, ctx->ap_pos, base + pos * 4, &entry);
+    if(ret != 1) {
+        DBGS("Read failed");
+        furi_mutex_release(ctx->swd_mutex);
+        return 0;
+    }
+    furi_mutex_release(ctx->swd_mutex);
+
+    return base + (entry & 0xFFFFF000);
+}
+
+bool adi_is_romtable(AppFSM* const ctx, uint32_t base) {
+    uint8_t class = 0;
+
+    if(!adi_get_class(ctx, base, &class)) {
+        return false;
+    }
+
+    if(class != CIDR_CLASS_ROMTABLE) {
+        return false;
+    }
+
+    return true;
+}

+ 34 - 0
swd_probe/adi.h

@@ -0,0 +1,34 @@
+
+#ifndef __ADI_H__
+#define __ADI_H__
+
+#include "swd_probe_app.h"
+
+#define ARM_ID 0x23B
+
+#define ARCH_ID(architect, archid) ((architect) << 21) | (archid)
+
+#define BIT(nr) (1UL << (nr))
+#define ARM_CS_C9_DEVARCH_PRESENT BIT(20)
+#define ARM_CS_C9_DEVARCH_ARCHITECT_MASK (0xFFE00000)
+#define ARM_CS_C9_DEVARCH_ARCHID_MASK (0x0000FFFF)
+#define DEVARCH_ID_MASK (ARM_CS_C9_DEVARCH_ARCHITECT_MASK | ARM_CS_C9_DEVARCH_ARCHID_MASK)
+
+typedef struct {
+    uint16_t designer;
+    uint16_t part;
+    uint8_t revision;
+    uint8_t cmod;
+    uint8_t revand;
+    uint8_t size;
+} pidr_data_t;
+
+typedef enum { CIDR_CLASS_ROMTABLE = 0x01, CIDR_CLASS_CORESIGHT = 0x09 } cidr_classes_t;
+
+uint32_t adi_romtable_entry_count(AppFSM* const ctx, uint32_t base);
+uint32_t adi_romtable_get(AppFSM* const ctx, uint32_t base, uint32_t pos);
+bool adi_is_romtable(AppFSM* const ctx, uint32_t base);
+const char* adi_romtable_type(AppFSM* const ctx, uint32_t base);
+const char* adi_romtable_full(AppFSM* const ctx, uint32_t base);
+
+#endif

+ 15 - 0
swd_probe/application.fam

@@ -0,0 +1,15 @@
+App(
+    appid="swd_probe",
+    name="SWD Probe",
+    apptype=FlipperAppType.EXTERNAL,
+    entry_point="swd_probe_app_main",
+    requires=["notification", "gui", "storage", "dialogs", "cli"],
+    stack_size=2 * 1024,
+    order=10,
+    fap_icon="icons/app.png",
+    fap_category="GPIO",
+    fap_icon_assets="icons",
+    fap_author="@g3gg0 & (fixes by @xMasterX)",
+    fap_version="1.1",
+    fap_description="ARM SWD (Single Wire Debug) Probe",
+)

BIN
swd_probe/icons/ButtonDown_7x4.png


BIN
swd_probe/icons/ButtonUp_7x4.png


BIN
swd_probe/icons/app.png


BIN
swd_probe/icons/swd.png


BIN
swd_probe/img/1.png


+ 26 - 0
swd_probe/jep106.c

@@ -0,0 +1,26 @@
+/* https://github.com/openocd-org/openocd/blob/master/src/helper/ */
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+/***************************************************************************
+ *   Copyright (C) 2015 Andreas Fritiofson                                 *
+ *   andreas.fritiofson@gmail.com                                          *
+ ***************************************************************************/
+
+#include "jep106.h"
+
+static const char* const jep106[][126] = {
+#include "jep106.inc"
+};
+
+const char* jep106_table_manufacturer(unsigned int bank, unsigned int id) {
+    if(id < 1 || id > 126) {
+        return "<invalid>";
+    }
+
+    /* index is zero based */
+    id--;
+
+    if(bank >= 14 || jep106[bank][id] == 0) return "<unknown>";
+
+    return jep106[bank][id];
+}

+ 26 - 0
swd_probe/jep106.h

@@ -0,0 +1,26 @@
+/* https://github.com/openocd-org/openocd/blob/master/src/helper/ */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/***************************************************************************
+ *   Copyright (C) 2015 Andreas Fritiofson                                 *
+ *   andreas.fritiofson@gmail.com                                          *
+ ***************************************************************************/
+
+#ifndef OPENOCD_HELPER_JEP106_H
+#define OPENOCD_HELPER_JEP106_H
+
+/**
+ * Get the manufacturer name associated with a JEP106 ID.
+ * @param bank The bank (number of continuation codes) of the manufacturer ID.
+ * @param id The 7-bit manufacturer ID (i.e. with parity stripped).
+ * @return A pointer to static const storage containing the name of the
+ *         manufacturer associated with bank and id, or one of the strings
+ *         "<invalid>" and "<unknown>".
+ */
+const char* jep106_table_manufacturer(unsigned int bank, unsigned int id);
+
+static inline const char* jep106_manufacturer(unsigned int manufacturer) {
+    return jep106_table_manufacturer(manufacturer >> 7, manufacturer & 0x7f);
+}
+
+#endif /* OPENOCD_HELPER_JEP106_H */

+ 1791 - 0
swd_probe/jep106.inc

@@ -0,0 +1,1791 @@
+/* https://github.com/openocd-org/openocd/blob/master/src/helper/ */
+
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+
+/*
+ * The manufacturer's standard identification code list appears in JEP106.
+ * Copyright (c) 2022 JEDEC. All rights reserved.
+ *
+ * JEP106 is regularly updated. For the current manufacturer's standard
+ * identification code list, please visit the JEDEC website at www.jedec.org .
+ */
+
+/* This file is aligned to revision JEP106BF.01 October 2022. */
+
+[0][0x01 - 1] = "AMD",
+[0][0x02 - 1] = "AMI",
+[0][0x03 - 1] = "Fairchild",
+[0][0x04 - 1] = "Fujitsu",
+[0][0x05 - 1] = "GTE",
+[0][0x06 - 1] = "Harris",
+[0][0x07 - 1] = "Hitachi",
+[0][0x08 - 1] = "Inmos",
+[0][0x09 - 1] = "Intel",
+[0][0x0a - 1] = "I.T.T.",
+[0][0x0b - 1] = "Intersil",
+[0][0x0c - 1] = "Monolithic Memories",
+[0][0x0d - 1] = "Mostek",
+[0][0x0e - 1] = "Freescale (Motorola)",
+[0][0x0f - 1] = "National",
+[0][0x10 - 1] = "NEC",
+[0][0x11 - 1] = "RCA",
+[0][0x12 - 1] = "Raytheon",
+[0][0x13 - 1] = "Conexant (Rockwell)",
+[0][0x14 - 1] = "Seeq",
+[0][0x15 - 1] = "NXP (Philips)",
+[0][0x16 - 1] = "Synertek",
+[0][0x17 - 1] = "Texas Instruments",
+[0][0x18 - 1] = "Kioxia Corporation",
+[0][0x19 - 1] = "Xicor",
+[0][0x1a - 1] = "Zilog",
+[0][0x1b - 1] = "Eurotechnique",
+[0][0x1c - 1] = "Mitsubishi",
+[0][0x1d - 1] = "Lucent (AT&T)",
+[0][0x1e - 1] = "Exel",
+[0][0x1f - 1] = "Atmel",
+[0][0x20 - 1] = "STMicroelectronics",
+[0][0x21 - 1] = "Lattice Semi.",
+[0][0x22 - 1] = "NCR",
+[0][0x23 - 1] = "Wafer Scale Integration",
+[0][0x24 - 1] = "IBM",
+[0][0x25 - 1] = "Tristar",
+[0][0x26 - 1] = "Visic",
+[0][0x27 - 1] = "Intl. CMOS Technology",
+[0][0x28 - 1] = "SSSI",
+[0][0x29 - 1] = "Microchip Technology",
+[0][0x2a - 1] = "Ricoh Ltd",
+[0][0x2b - 1] = "VLSI",
+[0][0x2c - 1] = "Micron Technology",
+[0][0x2d - 1] = "SK Hynix",
+[0][0x2e - 1] = "OKI Semiconductor",
+[0][0x2f - 1] = "ACTEL",
+[0][0x30 - 1] = "Sharp",
+[0][0x31 - 1] = "Catalyst",
+[0][0x32 - 1] = "Panasonic",
+[0][0x33 - 1] = "IDT",
+[0][0x34 - 1] = "Cypress",
+[0][0x35 - 1] = "DEC",
+[0][0x36 - 1] = "LSI Logic",
+[0][0x37 - 1] = "Zarlink (Plessey)",
+[0][0x38 - 1] = "UTMC",
+[0][0x39 - 1] = "Thinking Machine",
+[0][0x3a - 1] = "Thomson CSF",
+[0][0x3b - 1] = "Integrated CMOS (Vertex)",
+[0][0x3c - 1] = "Honeywell",
+[0][0x3d - 1] = "Tektronix",
+[0][0x3e - 1] = "Oracle Corporation",
+[0][0x3f - 1] = "Silicon Storage Technology",
+[0][0x40 - 1] = "ProMos/Mosel Vitelic",
+[0][0x41 - 1] = "Infineon (Siemens)",
+[0][0x42 - 1] = "Macronix",
+[0][0x43 - 1] = "Xerox",
+[0][0x44 - 1] = "Plus Logic",
+[0][0x45 - 1] = "Western Digital Technologies Inc",
+[0][0x46 - 1] = "Elan Circuit Tech.",
+[0][0x47 - 1] = "European Silicon Str.",
+[0][0x48 - 1] = "Apple Computer",
+[0][0x49 - 1] = "Xilinx",
+[0][0x4a - 1] = "Compaq",
+[0][0x4b - 1] = "Protocol Engines",
+[0][0x4c - 1] = "SCI",
+[0][0x4d - 1] = "Seiko Instruments",
+[0][0x4e - 1] = "Samsung",
+[0][0x4f - 1] = "I3 Design System",
+[0][0x50 - 1] = "Klic",
+[0][0x51 - 1] = "Crosspoint Solutions",
+[0][0x52 - 1] = "Alliance Memory Inc",
+[0][0x53 - 1] = "Tandem",
+[0][0x54 - 1] = "Hewlett-Packard",
+[0][0x55 - 1] = "Integrated Silicon Solutions",
+[0][0x56 - 1] = "Brooktree",
+[0][0x57 - 1] = "New Media",
+[0][0x58 - 1] = "MHS Electronic",
+[0][0x59 - 1] = "Performance Semi.",
+[0][0x5a - 1] = "Winbond Electronic",
+[0][0x5b - 1] = "Kawasaki Steel",
+[0][0x5c - 1] = "Bright Micro",
+[0][0x5d - 1] = "TECMAR",
+[0][0x5e - 1] = "Exar",
+[0][0x5f - 1] = "PCMCIA",
+[0][0x60 - 1] = "LG Semi (Goldstar)",
+[0][0x61 - 1] = "Northern Telecom",
+[0][0x62 - 1] = "Sanyo",
+[0][0x63 - 1] = "Array Microsystems",
+[0][0x64 - 1] = "Crystal Semiconductor",
+[0][0x65 - 1] = "Analog Devices",
+[0][0x66 - 1] = "PMC-Sierra",
+[0][0x67 - 1] = "Asparix",
+[0][0x68 - 1] = "Convex Computer",
+[0][0x69 - 1] = "Quality Semiconductor",
+[0][0x6a - 1] = "Nimbus Technology",
+[0][0x6b - 1] = "Transwitch",
+[0][0x6c - 1] = "Micronas (ITT Intermetall)",
+[0][0x6d - 1] = "Cannon",
+[0][0x6e - 1] = "Altera",
+[0][0x6f - 1] = "NEXCOM",
+[0][0x70 - 1] = "Qualcomm",
+[0][0x71 - 1] = "Sony",
+[0][0x72 - 1] = "Cray Research",
+[0][0x73 - 1] = "AMS(Austria Micro)",
+[0][0x74 - 1] = "Vitesse",
+[0][0x75 - 1] = "Aster Electronics",
+[0][0x76 - 1] = "Bay Networks (Synoptic)",
+[0][0x77 - 1] = "Zentrum/ZMD",
+[0][0x78 - 1] = "TRW",
+[0][0x79 - 1] = "Thesys",
+[0][0x7a - 1] = "Solbourne Computer",
+[0][0x7b - 1] = "Allied-Signal",
+[0][0x7c - 1] = "Dialog Semiconductor",
+[0][0x7d - 1] = "Media Vision",
+[0][0x7e - 1] = "Numonyx Corporation",
+[1][0x01 - 1] = "Cirrus Logic",
+[1][0x02 - 1] = "National Instruments",
+[1][0x03 - 1] = "ILC Data Device",
+[1][0x04 - 1] = "Alcatel Mietec",
+[1][0x05 - 1] = "Micro Linear",
+[1][0x06 - 1] = "Univ. of NC",
+[1][0x07 - 1] = "JTAG Technologies",
+[1][0x08 - 1] = "BAE Systems (Loral)",
+[1][0x09 - 1] = "Nchip",
+[1][0x0a - 1] = "Galileo Tech",
+[1][0x0b - 1] = "Bestlink Systems",
+[1][0x0c - 1] = "Graychip",
+[1][0x0d - 1] = "GENNUM",
+[1][0x0e - 1] = "Imagination Technologies Limited",
+[1][0x0f - 1] = "Robert Bosch",
+[1][0x10 - 1] = "Chip Express",
+[1][0x11 - 1] = "DATARAM",
+[1][0x12 - 1] = "United Microelectronics Corp",
+[1][0x13 - 1] = "TCSI",
+[1][0x14 - 1] = "Smart Modular",
+[1][0x15 - 1] = "Hughes Aircraft",
+[1][0x16 - 1] = "Lanstar Semiconductor",
+[1][0x17 - 1] = "Qlogic",
+[1][0x18 - 1] = "Kingston",
+[1][0x19 - 1] = "Music Semi",
+[1][0x1a - 1] = "Ericsson Components",
+[1][0x1b - 1] = "SpaSE",
+[1][0x1c - 1] = "Eon Silicon Devices",
+[1][0x1d - 1] = "Integrated Silicon Solution (ISSI)",
+[1][0x1e - 1] = "DoD",
+[1][0x1f - 1] = "Integ. Memories Tech.",
+[1][0x20 - 1] = "Corollary Inc",
+[1][0x21 - 1] = "Dallas Semiconductor",
+[1][0x22 - 1] = "Omnivision",
+[1][0x23 - 1] = "EIV(Switzerland)",
+[1][0x24 - 1] = "Novatel Wireless",
+[1][0x25 - 1] = "Zarlink (Mitel)",
+[1][0x26 - 1] = "Clearpoint",
+[1][0x27 - 1] = "Cabletron",
+[1][0x28 - 1] = "STEC (Silicon Tech)",
+[1][0x29 - 1] = "Vanguard",
+[1][0x2a - 1] = "Hagiwara Sys-Com",
+[1][0x2b - 1] = "Vantis",
+[1][0x2c - 1] = "Celestica",
+[1][0x2d - 1] = "Century",
+[1][0x2e - 1] = "Hal Computers",
+[1][0x2f - 1] = "Rohm Company Ltd",
+[1][0x30 - 1] = "Juniper Networks",
+[1][0x31 - 1] = "Libit Signal Processing",
+[1][0x32 - 1] = "Mushkin Enhanced Memory",
+[1][0x33 - 1] = "Tundra Semiconductor",
+[1][0x34 - 1] = "Adaptec Inc",
+[1][0x35 - 1] = "LightSpeed Semi.",
+[1][0x36 - 1] = "ZSP Corp",
+[1][0x37 - 1] = "AMIC Technology",
+[1][0x38 - 1] = "Adobe Systems",
+[1][0x39 - 1] = "Dynachip",
+[1][0x3a - 1] = "PNY Technologies Inc",
+[1][0x3b - 1] = "Newport Digital",
+[1][0x3c - 1] = "MMC Networks",
+[1][0x3d - 1] = "T Square",
+[1][0x3e - 1] = "Seiko Epson",
+[1][0x3f - 1] = "Broadcom",
+[1][0x40 - 1] = "Viking Components",
+[1][0x41 - 1] = "V3 Semiconductor",
+[1][0x42 - 1] = "Flextronics (Orbit Semiconductor)",
+[1][0x43 - 1] = "Suwa Electronics",
+[1][0x44 - 1] = "Transmeta",
+[1][0x45 - 1] = "Micron CMS",
+[1][0x46 - 1] = "American Computer & Digital Components Inc",
+[1][0x47 - 1] = "Enhance 3000 Inc",
+[1][0x48 - 1] = "Tower Semiconductor",
+[1][0x49 - 1] = "CPU Design",
+[1][0x4a - 1] = "Price Point",
+[1][0x4b - 1] = "Maxim Integrated Product",
+[1][0x4c - 1] = "Tellabs",
+[1][0x4d - 1] = "Centaur Technology",
+[1][0x4e - 1] = "Unigen Corporation",
+[1][0x4f - 1] = "Transcend Information",
+[1][0x50 - 1] = "Memory Card Technology",
+[1][0x51 - 1] = "CKD Corporation Ltd",
+[1][0x52 - 1] = "Capital Instruments Inc",
+[1][0x53 - 1] = "Aica Kogyo Ltd",
+[1][0x54 - 1] = "Linvex Technology",
+[1][0x55 - 1] = "MSC Vertriebs GmbH",
+[1][0x56 - 1] = "AKM Company Ltd",
+[1][0x57 - 1] = "Dynamem Inc",
+[1][0x58 - 1] = "NERA ASA",
+[1][0x59 - 1] = "GSI Technology",
+[1][0x5a - 1] = "Dane-Elec (C Memory)",
+[1][0x5b - 1] = "Acorn Computers",
+[1][0x5c - 1] = "Lara Technology",
+[1][0x5d - 1] = "Oak Technology Inc",
+[1][0x5e - 1] = "Itec Memory",
+[1][0x5f - 1] = "Tanisys Technology",
+[1][0x60 - 1] = "Truevision",
+[1][0x61 - 1] = "Wintec Industries",
+[1][0x62 - 1] = "Super PC Memory",
+[1][0x63 - 1] = "MGV Memory",
+[1][0x64 - 1] = "Galvantech",
+[1][0x65 - 1] = "Gadzoox Networks",
+[1][0x66 - 1] = "Multi Dimensional Cons.",
+[1][0x67 - 1] = "GateField",
+[1][0x68 - 1] = "Integrated Memory System",
+[1][0x69 - 1] = "Triscend",
+[1][0x6a - 1] = "XaQti",
+[1][0x6b - 1] = "Goldenram",
+[1][0x6c - 1] = "Clear Logic",
+[1][0x6d - 1] = "Cimaron Communications",
+[1][0x6e - 1] = "Nippon Steel Semi. Corp",
+[1][0x6f - 1] = "Advantage Memory",
+[1][0x70 - 1] = "AMCC",
+[1][0x71 - 1] = "LeCroy",
+[1][0x72 - 1] = "Yamaha Corporation",
+[1][0x73 - 1] = "Digital Microwave",
+[1][0x74 - 1] = "NetLogic Microsystems",
+[1][0x75 - 1] = "MIMOS Semiconductor",
+[1][0x76 - 1] = "Advanced Fibre",
+[1][0x77 - 1] = "BF Goodrich Data.",
+[1][0x78 - 1] = "Epigram",
+[1][0x79 - 1] = "Acbel Polytech Inc",
+[1][0x7a - 1] = "Apacer Technology",
+[1][0x7b - 1] = "Admor Memory",
+[1][0x7c - 1] = "FOXCONN",
+[1][0x7d - 1] = "Quadratics Superconductor",
+[1][0x7e - 1] = "3COM",
+[2][0x01 - 1] = "Camintonn Corporation",
+[2][0x02 - 1] = "ISOA Incorporated",
+[2][0x03 - 1] = "Agate Semiconductor",
+[2][0x04 - 1] = "ADMtek Incorporated",
+[2][0x05 - 1] = "HYPERTEC",
+[2][0x06 - 1] = "Adhoc Technologies",
+[2][0x07 - 1] = "MOSAID Technologies",
+[2][0x08 - 1] = "Ardent Technologies",
+[2][0x09 - 1] = "Switchcore",
+[2][0x0a - 1] = "Cisco Systems Inc",
+[2][0x0b - 1] = "Allayer Technologies",
+[2][0x0c - 1] = "WorkX AG (Wichman)",
+[2][0x0d - 1] = "Oasis Semiconductor",
+[2][0x0e - 1] = "Novanet Semiconductor",
+[2][0x0f - 1] = "E-M Solutions",
+[2][0x10 - 1] = "Power General",
+[2][0x11 - 1] = "Advanced Hardware Arch.",
+[2][0x12 - 1] = "Inova Semiconductors GmbH",
+[2][0x13 - 1] = "Telocity",
+[2][0x14 - 1] = "Delkin Devices",
+[2][0x15 - 1] = "Symagery Microsystems",
+[2][0x16 - 1] = "C-Port Corporation",
+[2][0x17 - 1] = "SiberCore Technologies",
+[2][0x18 - 1] = "Southland Microsystems",
+[2][0x19 - 1] = "Malleable Technologies",
+[2][0x1a - 1] = "Kendin Communications",
+[2][0x1b - 1] = "Great Technology Microcomputer",
+[2][0x1c - 1] = "Sanmina Corporation",
+[2][0x1d - 1] = "HADCO Corporation",
+[2][0x1e - 1] = "Corsair",
+[2][0x1f - 1] = "Actrans System Inc",
+[2][0x20 - 1] = "ALPHA Technologies",
+[2][0x21 - 1] = "Silicon Laboratories Inc (Cygnal)",
+[2][0x22 - 1] = "Artesyn Technologies",
+[2][0x23 - 1] = "Align Manufacturing",
+[2][0x24 - 1] = "Peregrine Semiconductor",
+[2][0x25 - 1] = "Chameleon Systems",
+[2][0x26 - 1] = "Aplus Flash Technology",
+[2][0x27 - 1] = "MIPS Technologies",
+[2][0x28 - 1] = "Chrysalis ITS",
+[2][0x29 - 1] = "ADTEC Corporation",
+[2][0x2a - 1] = "Kentron Technologies",
+[2][0x2b - 1] = "Win Technologies",
+[2][0x2c - 1] = "Tezzaron Semiconductor",
+[2][0x2d - 1] = "Extreme Packet Devices",
+[2][0x2e - 1] = "RF Micro Devices",
+[2][0x2f - 1] = "Siemens AG",
+[2][0x30 - 1] = "Sarnoff Corporation",
+[2][0x31 - 1] = "Itautec SA",
+[2][0x32 - 1] = "Radiata Inc",
+[2][0x33 - 1] = "Benchmark Elect. (AVEX)",
+[2][0x34 - 1] = "Legend",
+[2][0x35 - 1] = "SpecTek Incorporated",
+[2][0x36 - 1] = "Hi/fn",
+[2][0x37 - 1] = "Enikia Incorporated",
+[2][0x38 - 1] = "SwitchOn Networks",
+[2][0x39 - 1] = "AANetcom Incorporated",
+[2][0x3a - 1] = "Micro Memory Bank",
+[2][0x3b - 1] = "ESS Technology",
+[2][0x3c - 1] = "Virata Corporation",
+[2][0x3d - 1] = "Excess Bandwidth",
+[2][0x3e - 1] = "West Bay Semiconductor",
+[2][0x3f - 1] = "DSP Group",
+[2][0x40 - 1] = "Newport Communications",
+[2][0x41 - 1] = "Chip2Chip Incorporated",
+[2][0x42 - 1] = "Phobos Corporation",
+[2][0x43 - 1] = "Intellitech Corporation",
+[2][0x44 - 1] = "Nordic VLSI ASA",
+[2][0x45 - 1] = "Ishoni Networks",
+[2][0x46 - 1] = "Silicon Spice",
+[2][0x47 - 1] = "Alchemy Semiconductor",
+[2][0x48 - 1] = "Agilent Technologies",
+[2][0x49 - 1] = "Centillium Communications",
+[2][0x4a - 1] = "W.L. Gore",
+[2][0x4b - 1] = "HanBit Electronics",
+[2][0x4c - 1] = "GlobeSpan",
+[2][0x4d - 1] = "Element 14",
+[2][0x4e - 1] = "Pycon",
+[2][0x4f - 1] = "Saifun Semiconductors",
+[2][0x50 - 1] = "Sibyte Incorporated",
+[2][0x51 - 1] = "MetaLink Technologies",
+[2][0x52 - 1] = "Feiya Technology",
+[2][0x53 - 1] = "I & C Technology",
+[2][0x54 - 1] = "Shikatronics",
+[2][0x55 - 1] = "Elektrobit",
+[2][0x56 - 1] = "Megic",
+[2][0x57 - 1] = "Com-Tier",
+[2][0x58 - 1] = "Malaysia Micro Solutions",
+[2][0x59 - 1] = "Hyperchip",
+[2][0x5a - 1] = "Gemstone Communications",
+[2][0x5b - 1] = "Anadigm (Anadyne)",
+[2][0x5c - 1] = "3ParData",
+[2][0x5d - 1] = "Mellanox Technologies",
+[2][0x5e - 1] = "Tenx Technologies",
+[2][0x5f - 1] = "Helix AG",
+[2][0x60 - 1] = "Domosys",
+[2][0x61 - 1] = "Skyup Technology",
+[2][0x62 - 1] = "HiNT Corporation",
+[2][0x63 - 1] = "Chiaro",
+[2][0x64 - 1] = "MDT Technologies GmbH",
+[2][0x65 - 1] = "Exbit Technology A/S",
+[2][0x66 - 1] = "Integrated Technology Express",
+[2][0x67 - 1] = "AVED Memory",
+[2][0x68 - 1] = "Legerity",
+[2][0x69 - 1] = "Jasmine Networks",
+[2][0x6a - 1] = "Caspian Networks",
+[2][0x6b - 1] = "nCUBE",
+[2][0x6c - 1] = "Silicon Access Networks",
+[2][0x6d - 1] = "FDK Corporation",
+[2][0x6e - 1] = "High Bandwidth Access",
+[2][0x6f - 1] = "MultiLink Technology",
+[2][0x70 - 1] = "BRECIS",
+[2][0x71 - 1] = "World Wide Packets",
+[2][0x72 - 1] = "APW",
+[2][0x73 - 1] = "Chicory Systems",
+[2][0x74 - 1] = "Xstream Logic",
+[2][0x75 - 1] = "Fast-Chip",
+[2][0x76 - 1] = "Zucotto Wireless",
+[2][0x77 - 1] = "Realchip",
+[2][0x78 - 1] = "Galaxy Power",
+[2][0x79 - 1] = "eSilicon",
+[2][0x7a - 1] = "Morphics Technology",
+[2][0x7b - 1] = "Accelerant Networks",
+[2][0x7c - 1] = "Silicon Wave",
+[2][0x7d - 1] = "SandCraft",
+[2][0x7e - 1] = "Elpida",
+[3][0x01 - 1] = "Solectron",
+[3][0x02 - 1] = "Optosys Technologies",
+[3][0x03 - 1] = "Buffalo (Formerly Melco)",
+[3][0x04 - 1] = "TriMedia Technologies",
+[3][0x05 - 1] = "Cyan Technologies",
+[3][0x06 - 1] = "Global Locate",
+[3][0x07 - 1] = "Optillion",
+[3][0x08 - 1] = "Terago Communications",
+[3][0x09 - 1] = "Ikanos Communications",
+[3][0x0a - 1] = "Princeton Technology",
+[3][0x0b - 1] = "Nanya Technology",
+[3][0x0c - 1] = "Elite Flash Storage",
+[3][0x0d - 1] = "Mysticom",
+[3][0x0e - 1] = "LightSand Communications",
+[3][0x0f - 1] = "ATI Technologies",
+[3][0x10 - 1] = "Agere Systems",
+[3][0x11 - 1] = "NeoMagic",
+[3][0x12 - 1] = "AuroraNetics",
+[3][0x13 - 1] = "Golden Empire",
+[3][0x14 - 1] = "Mushkin",
+[3][0x15 - 1] = "Tioga Technologies",
+[3][0x16 - 1] = "Netlist",
+[3][0x17 - 1] = "TeraLogic",
+[3][0x18 - 1] = "Cicada Semiconductor",
+[3][0x19 - 1] = "Centon Electronics",
+[3][0x1a - 1] = "Tyco Electronics",
+[3][0x1b - 1] = "Magis Works",
+[3][0x1c - 1] = "Zettacom",
+[3][0x1d - 1] = "Cogency Semiconductor",
+[3][0x1e - 1] = "Chipcon AS",
+[3][0x1f - 1] = "Aspex Technology",
+[3][0x20 - 1] = "F5 Networks",
+[3][0x21 - 1] = "Programmable Silicon Solutions",
+[3][0x22 - 1] = "ChipWrights",
+[3][0x23 - 1] = "Acorn Networks",
+[3][0x24 - 1] = "Quicklogic",
+[3][0x25 - 1] = "Kingmax Semiconductor",
+[3][0x26 - 1] = "BOPS",
+[3][0x27 - 1] = "Flasys",
+[3][0x28 - 1] = "BitBlitz Communications",
+[3][0x29 - 1] = "eMemory Technology",
+[3][0x2a - 1] = "Procket Networks",
+[3][0x2b - 1] = "Purple Ray",
+[3][0x2c - 1] = "Trebia Networks",
+[3][0x2d - 1] = "Delta Electronics",
+[3][0x2e - 1] = "Onex Communications",
+[3][0x2f - 1] = "Ample Communications",
+[3][0x30 - 1] = "Memory Experts Intl",
+[3][0x31 - 1] = "Astute Networks",
+[3][0x32 - 1] = "Azanda Network Devices",
+[3][0x33 - 1] = "Dibcom",
+[3][0x34 - 1] = "Tekmos",
+[3][0x35 - 1] = "API NetWorks",
+[3][0x36 - 1] = "Bay Microsystems",
+[3][0x37 - 1] = "Firecron Ltd",
+[3][0x38 - 1] = "Resonext Communications",
+[3][0x39 - 1] = "Tachys Technologies",
+[3][0x3a - 1] = "Equator Technology",
+[3][0x3b - 1] = "Concept Computer",
+[3][0x3c - 1] = "SILCOM",
+[3][0x3d - 1] = "3Dlabs",
+[3][0x3e - 1] = "c't Magazine",
+[3][0x3f - 1] = "Sanera Systems",
+[3][0x40 - 1] = "Silicon Packets",
+[3][0x41 - 1] = "Viasystems Group",
+[3][0x42 - 1] = "Simtek",
+[3][0x43 - 1] = "Semicon Devices Singapore",
+[3][0x44 - 1] = "Satron Handelsges",
+[3][0x45 - 1] = "Improv Systems",
+[3][0x46 - 1] = "INDUSYS GmbH",
+[3][0x47 - 1] = "Corrent",
+[3][0x48 - 1] = "Infrant Technologies",
+[3][0x49 - 1] = "Ritek Corp",
+[3][0x4a - 1] = "empowerTel Networks",
+[3][0x4b - 1] = "Hypertec",
+[3][0x4c - 1] = "Cavium Networks",
+[3][0x4d - 1] = "PLX Technology",
+[3][0x4e - 1] = "Massana Design",
+[3][0x4f - 1] = "Intrinsity",
+[3][0x50 - 1] = "Valence Semiconductor",
+[3][0x51 - 1] = "Terawave Communications",
+[3][0x52 - 1] = "IceFyre Semiconductor",
+[3][0x53 - 1] = "Primarion",
+[3][0x54 - 1] = "Picochip Designs Ltd",
+[3][0x55 - 1] = "Silverback Systems",
+[3][0x56 - 1] = "Jade Star Technologies",
+[3][0x57 - 1] = "Pijnenburg Securealink",
+[3][0x58 - 1] = "takeMS - Ultron AG",
+[3][0x59 - 1] = "Cambridge Silicon Radio",
+[3][0x5a - 1] = "Swissbit",
+[3][0x5b - 1] = "Nazomi Communications",
+[3][0x5c - 1] = "eWave System",
+[3][0x5d - 1] = "Rockwell Collins",
+[3][0x5e - 1] = "Picocel Co Ltd (Paion)",
+[3][0x5f - 1] = "Alphamosaic Ltd",
+[3][0x60 - 1] = "Sandburst",
+[3][0x61 - 1] = "SiCon Video",
+[3][0x62 - 1] = "NanoAmp Solutions",
+[3][0x63 - 1] = "Ericsson Technology",
+[3][0x64 - 1] = "PrairieComm",
+[3][0x65 - 1] = "Mitac International",
+[3][0x66 - 1] = "Layer N Networks",
+[3][0x67 - 1] = "MtekVision (Atsana)",
+[3][0x68 - 1] = "Allegro Networks",
+[3][0x69 - 1] = "Marvell Semiconductors",
+[3][0x6a - 1] = "Netergy Microelectronic",
+[3][0x6b - 1] = "NVIDIA",
+[3][0x6c - 1] = "Internet Machines",
+[3][0x6d - 1] = "Memorysolution GmbH",
+[3][0x6e - 1] = "Litchfield Communication",
+[3][0x6f - 1] = "Accton Technology",
+[3][0x70 - 1] = "Teradiant Networks",
+[3][0x71 - 1] = "Scaleo Chip",
+[3][0x72 - 1] = "Cortina Systems",
+[3][0x73 - 1] = "RAM Components",
+[3][0x74 - 1] = "Raqia Networks",
+[3][0x75 - 1] = "ClearSpeed",
+[3][0x76 - 1] = "Matsushita Battery",
+[3][0x77 - 1] = "Xelerated",
+[3][0x78 - 1] = "SimpleTech",
+[3][0x79 - 1] = "Utron Technology",
+[3][0x7a - 1] = "Astec International",
+[3][0x7b - 1] = "AVM gmbH",
+[3][0x7c - 1] = "Redux Communications",
+[3][0x7d - 1] = "Dot Hill Systems",
+[3][0x7e - 1] = "TeraChip",
+[4][0x01 - 1] = "T-RAM Incorporated",
+[4][0x02 - 1] = "Innovics Wireless",
+[4][0x03 - 1] = "Teknovus",
+[4][0x04 - 1] = "KeyEye Communications",
+[4][0x05 - 1] = "Runcom Technologies",
+[4][0x06 - 1] = "RedSwitch",
+[4][0x07 - 1] = "Dotcast",
+[4][0x08 - 1] = "Silicon Mountain Memory",
+[4][0x09 - 1] = "Signia Technologies",
+[4][0x0a - 1] = "Pixim",
+[4][0x0b - 1] = "Galazar Networks",
+[4][0x0c - 1] = "White Electronic Designs",
+[4][0x0d - 1] = "Patriot Scientific",
+[4][0x0e - 1] = "Neoaxiom Corporation",
+[4][0x0f - 1] = "3Y Power Technology",
+[4][0x10 - 1] = "Scaleo Chip",
+[4][0x11 - 1] = "Potentia Power Systems",
+[4][0x12 - 1] = "C-guys Incorporated",
+[4][0x13 - 1] = "Digital Communications Technology Inc",
+[4][0x14 - 1] = "Silicon-Based Technology",
+[4][0x15 - 1] = "Fulcrum Microsystems",
+[4][0x16 - 1] = "Positivo Informatica Ltd",
+[4][0x17 - 1] = "XIOtech Corporation",
+[4][0x18 - 1] = "PortalPlayer",
+[4][0x19 - 1] = "Zhiying Software",
+[4][0x1a - 1] = "ParkerVision Inc",
+[4][0x1b - 1] = "Phonex Broadband",
+[4][0x1c - 1] = "Skyworks Solutions",
+[4][0x1d - 1] = "Entropic Communications",
+[4][0x1e - 1] = "I'M Intelligent Memory Ltd",
+[4][0x1f - 1] = "Zensys A/S",
+[4][0x20 - 1] = "Legend Silicon Corp",
+[4][0x21 - 1] = "Sci-worx GmbH",
+[4][0x22 - 1] = "SMSC (Standard Microsystems)",
+[4][0x23 - 1] = "Renesas Electronics",
+[4][0x24 - 1] = "Raza Microelectronics",
+[4][0x25 - 1] = "Phyworks",
+[4][0x26 - 1] = "MediaTek",
+[4][0x27 - 1] = "Non-cents Productions",
+[4][0x28 - 1] = "US Modular",
+[4][0x29 - 1] = "Wintegra Ltd",
+[4][0x2a - 1] = "Mathstar",
+[4][0x2b - 1] = "StarCore",
+[4][0x2c - 1] = "Oplus Technologies",
+[4][0x2d - 1] = "Mindspeed",
+[4][0x2e - 1] = "Just Young Computer",
+[4][0x2f - 1] = "Radia Communications",
+[4][0x30 - 1] = "OCZ",
+[4][0x31 - 1] = "Emuzed",
+[4][0x32 - 1] = "LOGIC Devices",
+[4][0x33 - 1] = "Inphi Corporation",
+[4][0x34 - 1] = "Quake Technologies",
+[4][0x35 - 1] = "Vixel",
+[4][0x36 - 1] = "SolusTek",
+[4][0x37 - 1] = "Kongsberg Maritime",
+[4][0x38 - 1] = "Faraday Technology",
+[4][0x39 - 1] = "Altium Ltd",
+[4][0x3a - 1] = "Insyte",
+[4][0x3b - 1] = "ARM Ltd",
+[4][0x3c - 1] = "DigiVision",
+[4][0x3d - 1] = "Vativ Technologies",
+[4][0x3e - 1] = "Endicott Interconnect Technologies",
+[4][0x3f - 1] = "Pericom",
+[4][0x40 - 1] = "Bandspeed",
+[4][0x41 - 1] = "LeWiz Communications",
+[4][0x42 - 1] = "CPU Technology",
+[4][0x43 - 1] = "Ramaxel Technology",
+[4][0x44 - 1] = "DSP Group",
+[4][0x45 - 1] = "Axis Communications",
+[4][0x46 - 1] = "Legacy Electronics",
+[4][0x47 - 1] = "Chrontel",
+[4][0x48 - 1] = "Powerchip Semiconductor",
+[4][0x49 - 1] = "MobilEye Technologies",
+[4][0x4a - 1] = "Excel Semiconductor",
+[4][0x4b - 1] = "A-DATA Technology",
+[4][0x4c - 1] = "VirtualDigm",
+[4][0x4d - 1] = "G Skill Intl",
+[4][0x4e - 1] = "Quanta Computer",
+[4][0x4f - 1] = "Yield Microelectronics",
+[4][0x50 - 1] = "Afa Technologies",
+[4][0x51 - 1] = "KINGBOX Technology Co Ltd",
+[4][0x52 - 1] = "Ceva",
+[4][0x53 - 1] = "iStor Networks",
+[4][0x54 - 1] = "Advance Modules",
+[4][0x55 - 1] = "Microsoft",
+[4][0x56 - 1] = "Open-Silicon",
+[4][0x57 - 1] = "Goal Semiconductor",
+[4][0x58 - 1] = "ARC International",
+[4][0x59 - 1] = "Simmtec",
+[4][0x5a - 1] = "Metanoia",
+[4][0x5b - 1] = "Key Stream",
+[4][0x5c - 1] = "Lowrance Electronics",
+[4][0x5d - 1] = "Adimos",
+[4][0x5e - 1] = "SiGe Semiconductor",
+[4][0x5f - 1] = "Fodus Communications",
+[4][0x60 - 1] = "Credence Systems Corp",
+[4][0x61 - 1] = "Genesis Microchip Inc",
+[4][0x62 - 1] = "Vihana Inc",
+[4][0x63 - 1] = "WIS Technologies",
+[4][0x64 - 1] = "GateChange Technologies",
+[4][0x65 - 1] = "High Density Devices AS",
+[4][0x66 - 1] = "Synopsys",
+[4][0x67 - 1] = "Gigaram",
+[4][0x68 - 1] = "Enigma Semiconductor Inc",
+[4][0x69 - 1] = "Century Micro Inc",
+[4][0x6a - 1] = "Icera Semiconductor",
+[4][0x6b - 1] = "Mediaworks Integrated Systems",
+[4][0x6c - 1] = "O'Neil Product Development",
+[4][0x6d - 1] = "Supreme Top Technology Ltd",
+[4][0x6e - 1] = "MicroDisplay Corporation",
+[4][0x6f - 1] = "Team Group Inc",
+[4][0x70 - 1] = "Sinett Corporation",
+[4][0x71 - 1] = "Toshiba Corporation",
+[4][0x72 - 1] = "Tensilica",
+[4][0x73 - 1] = "SiRF Technology",
+[4][0x74 - 1] = "Bacoc Inc",
+[4][0x75 - 1] = "SMaL Camera Technologies",
+[4][0x76 - 1] = "Thomson SC",
+[4][0x77 - 1] = "Airgo Networks",
+[4][0x78 - 1] = "Wisair Ltd",
+[4][0x79 - 1] = "SigmaTel",
+[4][0x7a - 1] = "Arkados",
+[4][0x7b - 1] = "Compete IT gmbH Co KG",
+[4][0x7c - 1] = "Eudar Technology Inc",
+[4][0x7d - 1] = "Focus Enhancements",
+[4][0x7e - 1] = "Xyratex",
+[5][0x01 - 1] = "Specular Networks",
+[5][0x02 - 1] = "Patriot Memory (PDP Systems)",
+[5][0x03 - 1] = "U-Chip Technology Corp",
+[5][0x04 - 1] = "Silicon Optix",
+[5][0x05 - 1] = "Greenfield Networks",
+[5][0x06 - 1] = "CompuRAM GmbH",
+[5][0x07 - 1] = "Stargen Inc",
+[5][0x08 - 1] = "NetCell Corporation",
+[5][0x09 - 1] = "Excalibrus Technologies Ltd",
+[5][0x0a - 1] = "SCM Microsystems",
+[5][0x0b - 1] = "Xsigo Systems Inc",
+[5][0x0c - 1] = "CHIPS & Systems Inc",
+[5][0x0d - 1] = "Tier 1 Multichip Solutions",
+[5][0x0e - 1] = "CWRL Labs",
+[5][0x0f - 1] = "Teradici",
+[5][0x10 - 1] = "Gigaram Inc",
+[5][0x11 - 1] = "g2 Microsystems",
+[5][0x12 - 1] = "PowerFlash Semiconductor",
+[5][0x13 - 1] = "P.A. Semi Inc",
+[5][0x14 - 1] = "NovaTech Solutions S.A.",
+[5][0x15 - 1] = "c2 Microsystems Inc",
+[5][0x16 - 1] = "Level5 Networks",
+[5][0x17 - 1] = "COS Memory AG",
+[5][0x18 - 1] = "Innovasic Semiconductor",
+[5][0x19 - 1] = "02IC Co Ltd",
+[5][0x1a - 1] = "Tabula Inc",
+[5][0x1b - 1] = "Crucial Technology",
+[5][0x1c - 1] = "Chelsio Communications",
+[5][0x1d - 1] = "Solarflare Communications",
+[5][0x1e - 1] = "Xambala Inc",
+[5][0x1f - 1] = "EADS Astrium",
+[5][0x20 - 1] = "Terra Semiconductor Inc",
+[5][0x21 - 1] = "Imaging Works Inc",
+[5][0x22 - 1] = "Astute Networks Inc",
+[5][0x23 - 1] = "Tzero",
+[5][0x24 - 1] = "Emulex",
+[5][0x25 - 1] = "Power-One",
+[5][0x26 - 1] = "Pulse~LINK Inc",
+[5][0x27 - 1] = "Hon Hai Precision Industry",
+[5][0x28 - 1] = "White Rock Networks Inc",
+[5][0x29 - 1] = "Telegent Systems USA Inc",
+[5][0x2a - 1] = "Atrua Technologies Inc",
+[5][0x2b - 1] = "Acbel Polytech Inc",
+[5][0x2c - 1] = "eRide Inc",
+[5][0x2d - 1] = "ULi Electronics Inc",
+[5][0x2e - 1] = "Magnum Semiconductor Inc",
+[5][0x2f - 1] = "neoOne Technology Inc",
+[5][0x30 - 1] = "Connex Technology Inc",
+[5][0x31 - 1] = "Stream Processors Inc",
+[5][0x32 - 1] = "Focus Enhancements",
+[5][0x33 - 1] = "Telecis Wireless Inc",
+[5][0x34 - 1] = "uNav Microelectronics",
+[5][0x35 - 1] = "Tarari Inc",
+[5][0x36 - 1] = "Ambric Inc",
+[5][0x37 - 1] = "Newport Media Inc",
+[5][0x38 - 1] = "VMTS",
+[5][0x39 - 1] = "Enuclia Semiconductor Inc",
+[5][0x3a - 1] = "Virtium Technology Inc",
+[5][0x3b - 1] = "Solid State System Co Ltd",
+[5][0x3c - 1] = "Kian Tech LLC",
+[5][0x3d - 1] = "Artimi",
+[5][0x3e - 1] = "Power Quotient International",
+[5][0x3f - 1] = "Avago Technologies",
+[5][0x40 - 1] = "ADTechnology",
+[5][0x41 - 1] = "Sigma Designs",
+[5][0x42 - 1] = "SiCortex Inc",
+[5][0x43 - 1] = "Ventura Technology Group",
+[5][0x44 - 1] = "eASIC",
+[5][0x45 - 1] = "M.H.S. SAS",
+[5][0x46 - 1] = "Micro Star International",
+[5][0x47 - 1] = "Rapport Inc",
+[5][0x48 - 1] = "Makway International",
+[5][0x49 - 1] = "Broad Reach Engineering Co",
+[5][0x4a - 1] = "Semiconductor Mfg Intl Corp",
+[5][0x4b - 1] = "SiConnect",
+[5][0x4c - 1] = "FCI USA Inc",
+[5][0x4d - 1] = "Validity Sensors",
+[5][0x4e - 1] = "Coney Technology Co Ltd",
+[5][0x4f - 1] = "Spans Logic",
+[5][0x50 - 1] = "Neterion Inc",
+[5][0x51 - 1] = "Qimonda",
+[5][0x52 - 1] = "New Japan Radio Co Ltd",
+[5][0x53 - 1] = "Velogix",
+[5][0x54 - 1] = "Montalvo Systems",
+[5][0x55 - 1] = "iVivity Inc",
+[5][0x56 - 1] = "Walton Chaintech",
+[5][0x57 - 1] = "AENEON",
+[5][0x58 - 1] = "Lorom Industrial Co Ltd",
+[5][0x59 - 1] = "Radiospire Networks",
+[5][0x5a - 1] = "Sensio Technologies Inc",
+[5][0x5b - 1] = "Nethra Imaging",
+[5][0x5c - 1] = "Hexon Technology Pte Ltd",
+[5][0x5d - 1] = "CompuStocx (CSX)",
+[5][0x5e - 1] = "Methode Electronics Inc",
+[5][0x5f - 1] = "Connect One Ltd",
+[5][0x60 - 1] = "Opulan Technologies",
+[5][0x61 - 1] = "Septentrio NV",
+[5][0x62 - 1] = "Goldenmars Technology Inc",
+[5][0x63 - 1] = "Kreton Corporation",
+[5][0x64 - 1] = "Cochlear Ltd",
+[5][0x65 - 1] = "Altair Semiconductor",
+[5][0x66 - 1] = "NetEffect Inc",
+[5][0x67 - 1] = "Spansion Inc",
+[5][0x68 - 1] = "Taiwan Semiconductor Mfg",
+[5][0x69 - 1] = "Emphany Systems Inc",
+[5][0x6a - 1] = "ApaceWave Technologies",
+[5][0x6b - 1] = "Mobilygen Corporation",
+[5][0x6c - 1] = "Tego",
+[5][0x6d - 1] = "Cswitch Corporation",
+[5][0x6e - 1] = "Haier (Beijing) IC Design Co",
+[5][0x6f - 1] = "MetaRAM",
+[5][0x70 - 1] = "Axel Electronics Co Ltd",
+[5][0x71 - 1] = "Tilera Corporation",
+[5][0x72 - 1] = "Aquantia",
+[5][0x73 - 1] = "Vivace Semiconductor",
+[5][0x74 - 1] = "Redpine Signals",
+[5][0x75 - 1] = "Octalica",
+[5][0x76 - 1] = "InterDigital Communications",
+[5][0x77 - 1] = "Avant Technology",
+[5][0x78 - 1] = "Asrock Inc",
+[5][0x79 - 1] = "Availink",
+[5][0x7a - 1] = "Quartics Inc",
+[5][0x7b - 1] = "Element CXI",
+[5][0x7c - 1] = "Innovaciones Microelectronicas",
+[5][0x7d - 1] = "VeriSilicon Microelectronics",
+[5][0x7e - 1] = "W5 Networks",
+[6][0x01 - 1] = "MOVEKING",
+[6][0x02 - 1] = "Mavrix Technology Inc",
+[6][0x03 - 1] = "CellGuide Ltd",
+[6][0x04 - 1] = "Faraday Technology",
+[6][0x05 - 1] = "Diablo Technologies Inc",
+[6][0x06 - 1] = "Jennic",
+[6][0x07 - 1] = "Octasic",
+[6][0x08 - 1] = "Molex Incorporated",
+[6][0x09 - 1] = "3Leaf Networks",
+[6][0x0a - 1] = "Bright Micron Technology",
+[6][0x0b - 1] = "Netxen",
+[6][0x0c - 1] = "NextWave Broadband Inc",
+[6][0x0d - 1] = "DisplayLink",
+[6][0x0e - 1] = "ZMOS Technology",
+[6][0x0f - 1] = "Tec-Hill",
+[6][0x10 - 1] = "Multigig Inc",
+[6][0x11 - 1] = "Amimon",
+[6][0x12 - 1] = "Euphonic Technologies Inc",
+[6][0x13 - 1] = "BRN Phoenix",
+[6][0x14 - 1] = "InSilica",
+[6][0x15 - 1] = "Ember Corporation",
+[6][0x16 - 1] = "Avexir Technologies Corporation",
+[6][0x17 - 1] = "Echelon Corporation",
+[6][0x18 - 1] = "Edgewater Computer Systems",
+[6][0x19 - 1] = "XMOS Semiconductor Ltd",
+[6][0x1a - 1] = "GENUSION Inc",
+[6][0x1b - 1] = "Memory Corp NV",
+[6][0x1c - 1] = "SiliconBlue Technologies",
+[6][0x1d - 1] = "Rambus Inc",
+[6][0x1e - 1] = "Andes Technology Corporation",
+[6][0x1f - 1] = "Coronis Systems",
+[6][0x20 - 1] = "Achronix Semiconductor",
+[6][0x21 - 1] = "Siano Mobile Silicon Ltd",
+[6][0x22 - 1] = "Semtech Corporation",
+[6][0x23 - 1] = "Pixelworks Inc",
+[6][0x24 - 1] = "Gaisler Research AB",
+[6][0x25 - 1] = "Teranetics",
+[6][0x26 - 1] = "Toppan Printing Co Ltd",
+[6][0x27 - 1] = "Kingxcon",
+[6][0x28 - 1] = "Silicon Integrated Systems",
+[6][0x29 - 1] = "I-O Data Device Inc",
+[6][0x2a - 1] = "NDS Americas Inc",
+[6][0x2b - 1] = "Solomon Systech Limited",
+[6][0x2c - 1] = "On Demand Microelectronics",
+[6][0x2d - 1] = "Amicus Wireless Inc",
+[6][0x2e - 1] = "SMARDTV SNC",
+[6][0x2f - 1] = "Comsys Communication Ltd",
+[6][0x30 - 1] = "Movidia Ltd",
+[6][0x31 - 1] = "Javad GNSS Inc",
+[6][0x32 - 1] = "Montage Technology Group",
+[6][0x33 - 1] = "Trident Microsystems",
+[6][0x34 - 1] = "Super Talent",
+[6][0x35 - 1] = "Optichron Inc",
+[6][0x36 - 1] = "Future Waves UK Ltd",
+[6][0x37 - 1] = "SiBEAM Inc",
+[6][0x38 - 1] = "InicoreInc",
+[6][0x39 - 1] = "Virident Systems",
+[6][0x3a - 1] = "M2000 Inc",
+[6][0x3b - 1] = "ZeroG Wireless Inc",
+[6][0x3c - 1] = "Gingle Technology Co Ltd",
+[6][0x3d - 1] = "Space Micro Inc",
+[6][0x3e - 1] = "Wilocity",
+[6][0x3f - 1] = "Novafora Inc",
+[6][0x40 - 1] = "iKoa Corporation",
+[6][0x41 - 1] = "ASint Technology",
+[6][0x42 - 1] = "Ramtron",
+[6][0x43 - 1] = "Plato Networks Inc",
+[6][0x44 - 1] = "IPtronics AS",
+[6][0x45 - 1] = "Infinite-Memories",
+[6][0x46 - 1] = "Parade Technologies Inc",
+[6][0x47 - 1] = "Dune Networks",
+[6][0x48 - 1] = "GigaDevice Semiconductor",
+[6][0x49 - 1] = "Modu Ltd",
+[6][0x4a - 1] = "CEITEC",
+[6][0x4b - 1] = "Northrop Grumman",
+[6][0x4c - 1] = "XRONET Corporation",
+[6][0x4d - 1] = "Sicon Semiconductor AB",
+[6][0x4e - 1] = "Atla Electronics Co Ltd",
+[6][0x4f - 1] = "TOPRAM Technology",
+[6][0x50 - 1] = "Silego Technology Inc",
+[6][0x51 - 1] = "Kinglife",
+[6][0x52 - 1] = "Ability Industries Ltd",
+[6][0x53 - 1] = "Silicon Power Computer & Communications",
+[6][0x54 - 1] = "Augusta Technology Inc",
+[6][0x55 - 1] = "Nantronics Semiconductors",
+[6][0x56 - 1] = "Hilscher Gesellschaft",
+[6][0x57 - 1] = "Quixant Ltd",
+[6][0x58 - 1] = "Percello Ltd",
+[6][0x59 - 1] = "NextIO Inc",
+[6][0x5a - 1] = "Scanimetrics Inc",
+[6][0x5b - 1] = "FS-Semi Company Ltd",
+[6][0x5c - 1] = "Infinera Corporation",
+[6][0x5d - 1] = "SandForce Inc",
+[6][0x5e - 1] = "Lexar Media",
+[6][0x5f - 1] = "Teradyne Inc",
+[6][0x60 - 1] = "Memory Exchange Corp",
+[6][0x61 - 1] = "Suzhou Smartek Electronics",
+[6][0x62 - 1] = "Avantium Corporation",
+[6][0x63 - 1] = "ATP Electronics Inc",
+[6][0x64 - 1] = "Valens Semiconductor Ltd",
+[6][0x65 - 1] = "Agate Logic Inc",
+[6][0x66 - 1] = "Netronome",
+[6][0x67 - 1] = "Zenverge Inc",
+[6][0x68 - 1] = "N-trig Ltd",
+[6][0x69 - 1] = "SanMax Technologies Inc",
+[6][0x6a - 1] = "Contour Semiconductor Inc",
+[6][0x6b - 1] = "TwinMOS",
+[6][0x6c - 1] = "Silicon Systems Inc",
+[6][0x6d - 1] = "V-Color Technology Inc",
+[6][0x6e - 1] = "Certicom Corporation",
+[6][0x6f - 1] = "JSC ICC Milandr",
+[6][0x70 - 1] = "PhotoFast Global Inc",
+[6][0x71 - 1] = "InnoDisk Corporation",
+[6][0x72 - 1] = "Muscle Power",
+[6][0x73 - 1] = "Energy Micro",
+[6][0x74 - 1] = "Innofidei",
+[6][0x75 - 1] = "CopperGate Communications",
+[6][0x76 - 1] = "Holtek Semiconductor Inc",
+[6][0x77 - 1] = "Myson Century Inc",
+[6][0x78 - 1] = "FIDELIX",
+[6][0x79 - 1] = "Red Digital Cinema",
+[6][0x7a - 1] = "Densbits Technology",
+[6][0x7b - 1] = "Zempro",
+[6][0x7c - 1] = "MoSys",
+[6][0x7d - 1] = "Provigent",
+[6][0x7e - 1] = "Triad Semiconductor Inc",
+[7][0x01 - 1] = "Siklu Communication Ltd",
+[7][0x02 - 1] = "A Force Manufacturing Ltd",
+[7][0x03 - 1] = "Strontium",
+[7][0x04 - 1] = "ALi Corp (Abilis Systems)",
+[7][0x05 - 1] = "Siglead Inc",
+[7][0x06 - 1] = "Ubicom Inc",
+[7][0x07 - 1] = "Unifosa Corporation",
+[7][0x08 - 1] = "Stretch Inc",
+[7][0x09 - 1] = "Lantiq Deutschland GmbH",
+[7][0x0a - 1] = "Visipro.",
+[7][0x0b - 1] = "EKMemory",
+[7][0x0c - 1] = "Microelectronics Institute ZTE",
+[7][0x0d - 1] = "u-blox AG",
+[7][0x0e - 1] = "Carry Technology Co Ltd",
+[7][0x0f - 1] = "Nokia",
+[7][0x10 - 1] = "King Tiger Technology",
+[7][0x11 - 1] = "Sierra Wireless",
+[7][0x12 - 1] = "HT Micron",
+[7][0x13 - 1] = "Albatron Technology Co Ltd",
+[7][0x14 - 1] = "Leica Geosystems AG",
+[7][0x15 - 1] = "BroadLight",
+[7][0x16 - 1] = "AEXEA",
+[7][0x17 - 1] = "ClariPhy Communications Inc",
+[7][0x18 - 1] = "Green Plug",
+[7][0x19 - 1] = "Design Art Networks",
+[7][0x1a - 1] = "Mach Xtreme Technology Ltd",
+[7][0x1b - 1] = "ATO Solutions Co Ltd",
+[7][0x1c - 1] = "Ramsta",
+[7][0x1d - 1] = "Greenliant Systems Ltd",
+[7][0x1e - 1] = "Teikon",
+[7][0x1f - 1] = "Antec Hadron",
+[7][0x20 - 1] = "NavCom Technology Inc",
+[7][0x21 - 1] = "Shanghai Fudan Microelectronics",
+[7][0x22 - 1] = "Calxeda Inc",
+[7][0x23 - 1] = "JSC EDC Electronics",
+[7][0x24 - 1] = "Kandit Technology Co Ltd",
+[7][0x25 - 1] = "Ramos Technology",
+[7][0x26 - 1] = "Goldenmars Technology",
+[7][0x27 - 1] = "XeL Technology Inc",
+[7][0x28 - 1] = "Newzone Corporation",
+[7][0x29 - 1] = "ShenZhen MercyPower Tech",
+[7][0x2a - 1] = "Nanjing Yihuo Technology",
+[7][0x2b - 1] = "Nethra Imaging Inc",
+[7][0x2c - 1] = "SiTel Semiconductor BV",
+[7][0x2d - 1] = "SolidGear Corporation",
+[7][0x2e - 1] = "Topower Computer Ind Co Ltd",
+[7][0x2f - 1] = "Wilocity",
+[7][0x30 - 1] = "Profichip GmbH",
+[7][0x31 - 1] = "Gerad Technologies",
+[7][0x32 - 1] = "Ritek Corporation",
+[7][0x33 - 1] = "Gomos Technology Limited",
+[7][0x34 - 1] = "Memoright Corporation",
+[7][0x35 - 1] = "D-Broad Inc",
+[7][0x36 - 1] = "HiSilicon Technologies",
+[7][0x37 - 1] = "Syndiant Inc.",
+[7][0x38 - 1] = "Enverv Inc",
+[7][0x39 - 1] = "Cognex",
+[7][0x3a - 1] = "Xinnova Technology Inc",
+[7][0x3b - 1] = "Ultron AG",
+[7][0x3c - 1] = "Concord Idea Corporation",
+[7][0x3d - 1] = "AIM Corporation",
+[7][0x3e - 1] = "Lifetime Memory Products",
+[7][0x3f - 1] = "Ramsway",
+[7][0x40 - 1] = "Recore Systems B.V.",
+[7][0x41 - 1] = "Haotian Jinshibo Science Tech",
+[7][0x42 - 1] = "Being Advanced Memory",
+[7][0x43 - 1] = "Adesto Technologies",
+[7][0x44 - 1] = "Giantec Semiconductor Inc",
+[7][0x45 - 1] = "HMD Electronics AG",
+[7][0x46 - 1] = "Gloway International (HK)",
+[7][0x47 - 1] = "Kingcore",
+[7][0x48 - 1] = "Anucell Technology Holding",
+[7][0x49 - 1] = "Accord Software & Systems Pvt. Ltd",
+[7][0x4a - 1] = "Active-Semi Inc",
+[7][0x4b - 1] = "Denso Corporation",
+[7][0x4c - 1] = "TLSI Inc",
+[7][0x4d - 1] = "Qidan",
+[7][0x4e - 1] = "Mustang",
+[7][0x4f - 1] = "Orca Systems",
+[7][0x50 - 1] = "Passif Semiconductor",
+[7][0x51 - 1] = "GigaDevice Semiconductor (Beijing) Inc",
+[7][0x52 - 1] = "Memphis Electronic",
+[7][0x53 - 1] = "Beckhoff Automation GmbH",
+[7][0x54 - 1] = "Harmony Semiconductor Corp",
+[7][0x55 - 1] = "Air Computers SRL",
+[7][0x56 - 1] = "TMT Memory",
+[7][0x57 - 1] = "Eorex Corporation",
+[7][0x58 - 1] = "Xingtera",
+[7][0x59 - 1] = "Netsol",
+[7][0x5a - 1] = "Bestdon Technology Co Ltd",
+[7][0x5b - 1] = "Baysand Inc",
+[7][0x5c - 1] = "Uroad Technology Co Ltd",
+[7][0x5d - 1] = "Wilk Elektronik S.A.",
+[7][0x5e - 1] = "AAI",
+[7][0x5f - 1] = "Harman",
+[7][0x60 - 1] = "Berg Microelectronics Inc",
+[7][0x61 - 1] = "ASSIA Inc",
+[7][0x62 - 1] = "Visiontek Products LLC",
+[7][0x63 - 1] = "OCMEMORY",
+[7][0x64 - 1] = "Welink Solution Inc",
+[7][0x65 - 1] = "Shark Gaming",
+[7][0x66 - 1] = "Avalanche Technology",
+[7][0x67 - 1] = "R&D Center ELVEES OJSC",
+[7][0x68 - 1] = "KingboMars Technology Co Ltd",
+[7][0x69 - 1] = "High Bridge Solutions Industria Eletronica",
+[7][0x6a - 1] = "Transcend Technology Co Ltd",
+[7][0x6b - 1] = "Everspin Technologies",
+[7][0x6c - 1] = "Hon-Hai Precision",
+[7][0x6d - 1] = "Smart Storage Systems",
+[7][0x6e - 1] = "Toumaz Group",
+[7][0x6f - 1] = "Zentel Electronics Corporation",
+[7][0x70 - 1] = "Panram International Corporation",
+[7][0x71 - 1] = "Silicon Space Technology",
+[7][0x72 - 1] = "LITE-ON IT Corporation",
+[7][0x73 - 1] = "Inuitive",
+[7][0x74 - 1] = "HMicro",
+[7][0x75 - 1] = "BittWare Inc",
+[7][0x76 - 1] = "GLOBALFOUNDRIES",
+[7][0x77 - 1] = "ACPI Digital Co Ltd",
+[7][0x78 - 1] = "Annapurna Labs",
+[7][0x79 - 1] = "AcSiP Technology Corporation",
+[7][0x7a - 1] = "Idea! Electronic Systems",
+[7][0x7b - 1] = "Gowe Technology Co Ltd",
+[7][0x7c - 1] = "Hermes Testing Solutions Inc",
+[7][0x7d - 1] = "Positivo BGH",
+[7][0x7e - 1] = "Intelligence Silicon Technology",
+[8][0x01 - 1] = "3D PLUS",
+[8][0x02 - 1] = "Diehl Aerospace",
+[8][0x03 - 1] = "Fairchild",
+[8][0x04 - 1] = "Mercury Systems",
+[8][0x05 - 1] = "Sonics Inc",
+[8][0x06 - 1] = "Emerson Automation Solutions",
+[8][0x07 - 1] = "Shenzhen Jinge Information Co Ltd",
+[8][0x08 - 1] = "SCWW",
+[8][0x09 - 1] = "Silicon Motion Inc",
+[8][0x0a - 1] = "Anurag",
+[8][0x0b - 1] = "King Kong",
+[8][0x0c - 1] = "FROM30 Co Ltd",
+[8][0x0d - 1] = "Gowin Semiconductor Corp",
+[8][0x0e - 1] = "Fremont Micro Devices Ltd",
+[8][0x0f - 1] = "Ericsson Modems",
+[8][0x10 - 1] = "Exelis",
+[8][0x11 - 1] = "Satixfy Ltd",
+[8][0x12 - 1] = "Galaxy Microsystems Ltd",
+[8][0x13 - 1] = "Gloway International Co Ltd",
+[8][0x14 - 1] = "Lab",
+[8][0x15 - 1] = "Smart Energy Instruments",
+[8][0x16 - 1] = "Approved Memory Corporation",
+[8][0x17 - 1] = "Axell Corporation",
+[8][0x18 - 1] = "Essencore Limited",
+[8][0x19 - 1] = "Phytium",
+[8][0x1a - 1] = "Xi'an UniIC Semiconductors Co Ltd",
+[8][0x1b - 1] = "Ambiq Micro",
+[8][0x1c - 1] = "eveRAM Technology Inc",
+[8][0x1d - 1] = "Infomax",
+[8][0x1e - 1] = "Butterfly Network Inc",
+[8][0x1f - 1] = "Shenzhen City Gcai Electronics",
+[8][0x20 - 1] = "Stack Devices Corporation",
+[8][0x21 - 1] = "ADK Media Group",
+[8][0x22 - 1] = "TSP Global Co Ltd",
+[8][0x23 - 1] = "HighX",
+[8][0x24 - 1] = "Shenzhen Elicks Technology",
+[8][0x25 - 1] = "XinKai/Silicon Kaiser",
+[8][0x26 - 1] = "Google Inc",
+[8][0x27 - 1] = "Dasima International Development",
+[8][0x28 - 1] = "Leahkinn Technology Limited",
+[8][0x29 - 1] = "HIMA Paul Hildebrandt GmbH Co KG",
+[8][0x2a - 1] = "Keysight Technologies",
+[8][0x2b - 1] = "Techcomp International (Fastable)",
+[8][0x2c - 1] = "Ancore Technology Corporation",
+[8][0x2d - 1] = "Nuvoton",
+[8][0x2e - 1] = "Korea Uhbele International Group Ltd",
+[8][0x2f - 1] = "Ikegami Tsushinki Co Ltd",
+[8][0x30 - 1] = "RelChip Inc",
+[8][0x31 - 1] = "Baikal Electronics",
+[8][0x32 - 1] = "Nemostech Inc",
+[8][0x33 - 1] = "Memorysolution GmbH",
+[8][0x34 - 1] = "Silicon Integrated Systems Corporation",
+[8][0x35 - 1] = "Xiede",
+[8][0x36 - 1] = "BRC",
+[8][0x37 - 1] = "Flash Chi",
+[8][0x38 - 1] = "Jone",
+[8][0x39 - 1] = "GCT Semiconductor Inc",
+[8][0x3a - 1] = "Hong Kong Zetta Device Technology",
+[8][0x3b - 1] = "Unimemory Technology(s) Pte Ltd",
+[8][0x3c - 1] = "Cuso",
+[8][0x3d - 1] = "Kuso",
+[8][0x3e - 1] = "Uniquify Inc",
+[8][0x3f - 1] = "Skymedi Corporation",
+[8][0x40 - 1] = "Core Chance Co Ltd",
+[8][0x41 - 1] = "Tekism Co Ltd",
+[8][0x42 - 1] = "Seagate Technology PLC",
+[8][0x43 - 1] = "Hong Kong Gaia Group Co Limited",
+[8][0x44 - 1] = "Gigacom Semiconductor LLC",
+[8][0x45 - 1] = "V2 Technologies",
+[8][0x46 - 1] = "TLi",
+[8][0x47 - 1] = "Neotion",
+[8][0x48 - 1] = "Lenovo",
+[8][0x49 - 1] = "Shenzhen Zhongteng Electronic Corp Ltd",
+[8][0x4a - 1] = "Compound Photonics",
+[8][0x4b - 1] = "in2H2 inc",
+[8][0x4c - 1] = "Shenzhen Pango Microsystems Co Ltd",
+[8][0x4d - 1] = "Vasekey",
+[8][0x4e - 1] = "Cal-Comp Industria de Semicondutores",
+[8][0x4f - 1] = "Eyenix Co Ltd",
+[8][0x50 - 1] = "Heoriady",
+[8][0x51 - 1] = "Accelerated Memory Production Inc",
+[8][0x52 - 1] = "INVECAS Inc",
+[8][0x53 - 1] = "AP Memory",
+[8][0x54 - 1] = "Douqi Technology",
+[8][0x55 - 1] = "Etron Technology Inc",
+[8][0x56 - 1] = "Indie Semiconductor",
+[8][0x57 - 1] = "Socionext Inc",
+[8][0x58 - 1] = "HGST",
+[8][0x59 - 1] = "EVGA",
+[8][0x5a - 1] = "Audience Inc",
+[8][0x5b - 1] = "EpicGear",
+[8][0x5c - 1] = "Vitesse Enterprise Co",
+[8][0x5d - 1] = "Foxtronn International Corporation",
+[8][0x5e - 1] = "Bretelon Inc",
+[8][0x5f - 1] = "Graphcore",
+[8][0x60 - 1] = "Eoplex Inc",
+[8][0x61 - 1] = "MaxLinear Inc",
+[8][0x62 - 1] = "ETA Devices",
+[8][0x63 - 1] = "LOKI",
+[8][0x64 - 1] = "IMS Electronics Co Ltd",
+[8][0x65 - 1] = "Dosilicon Co Ltd",
+[8][0x66 - 1] = "Dolphin Integration",
+[8][0x67 - 1] = "Shenzhen Mic Electronics Technolog",
+[8][0x68 - 1] = "Boya Microelectronics Inc",
+[8][0x69 - 1] = "Geniachip (Roche)",
+[8][0x6a - 1] = "Axign",
+[8][0x6b - 1] = "Kingred Electronic Technology Ltd",
+[8][0x6c - 1] = "Chao Yue Zhuo Computer Business Dept.",
+[8][0x6d - 1] = "Guangzhou Si Nuo Electronic Technology.",
+[8][0x6e - 1] = "Crocus Technology Inc",
+[8][0x6f - 1] = "Creative Chips GmbH",
+[8][0x70 - 1] = "GE Aviation Systems LLC.",
+[8][0x71 - 1] = "Asgard",
+[8][0x72 - 1] = "Good Wealth Technology Ltd",
+[8][0x73 - 1] = "TriCor Technologies",
+[8][0x74 - 1] = "Nova-Systems GmbH",
+[8][0x75 - 1] = "JUHOR",
+[8][0x76 - 1] = "Zhuhai Douke Commerce Co Ltd",
+[8][0x77 - 1] = "DSL Memory",
+[8][0x78 - 1] = "Anvo-Systems Dresden GmbH",
+[8][0x79 - 1] = "Realtek",
+[8][0x7a - 1] = "AltoBeam",
+[8][0x7b - 1] = "Wave Computing",
+[8][0x7c - 1] = "Beijing TrustNet Technology Co Ltd",
+[8][0x7d - 1] = "Innovium Inc",
+[8][0x7e - 1] = "Starsway Technology Limited",
+[9][0x01 - 1] = "Weltronics Co LTD",
+[9][0x02 - 1] = "VMware Inc",
+[9][0x03 - 1] = "Hewlett Packard Enterprise",
+[9][0x04 - 1] = "INTENSO",
+[9][0x05 - 1] = "Puya Semiconductor",
+[9][0x06 - 1] = "MEMORFI",
+[9][0x07 - 1] = "MSC Technologies GmbH",
+[9][0x08 - 1] = "Txrui",
+[9][0x09 - 1] = "SiFive Inc",
+[9][0x0a - 1] = "Spreadtrum Communications",
+[9][0x0b - 1] = "XTX Technology Limited",
+[9][0x0c - 1] = "UMAX Technology",
+[9][0x0d - 1] = "Shenzhen Yong Sheng Technology",
+[9][0x0e - 1] = "SNOAMOO (Shenzhen Kai Zhuo Yue)",
+[9][0x0f - 1] = "Daten Tecnologia LTDA",
+[9][0x10 - 1] = "Shenzhen XinRuiYan Electronics",
+[9][0x11 - 1] = "Eta Compute",
+[9][0x12 - 1] = "Energous",
+[9][0x13 - 1] = "Raspberry Pi Trading Ltd",
+[9][0x14 - 1] = "Shenzhen Chixingzhe Tech Co Ltd",
+[9][0x15 - 1] = "Silicon Mobility",
+[9][0x16 - 1] = "IQ-Analog Corporation",
+[9][0x17 - 1] = "Uhnder Inc",
+[9][0x18 - 1] = "Impinj",
+[9][0x19 - 1] = "DEPO Computers",
+[9][0x1a - 1] = "Nespeed Sysems",
+[9][0x1b - 1] = "Yangtze Memory Technologies Co Ltd",
+[9][0x1c - 1] = "MemxPro Inc",
+[9][0x1d - 1] = "Tammuz Co Ltd",
+[9][0x1e - 1] = "Allwinner Technology",
+[9][0x1f - 1] = "Shenzhen City Futian District Qing Xuan Tong Computer Trading Firm",
+[9][0x20 - 1] = "XMC",
+[9][0x21 - 1] = "Teclast",
+[9][0x22 - 1] = "Maxsun",
+[9][0x23 - 1] = "Haiguang Integrated Circuit Design",
+[9][0x24 - 1] = "RamCENTER Technology",
+[9][0x25 - 1] = "Phison Electronics Corporation",
+[9][0x26 - 1] = "Guizhou Huaxintong Semi-Conductor",
+[9][0x27 - 1] = "Network Intelligence",
+[9][0x28 - 1] = "Continental Technology (Holdings)",
+[9][0x29 - 1] = "Guangzhou Huayan Suning Electronic",
+[9][0x2a - 1] = "Guangzhou Zhouji Electronic Co Ltd",
+[9][0x2b - 1] = "Shenzhen Giant Hui Kang Tech Co Ltd",
+[9][0x2c - 1] = "Shenzhen Yilong Innovative Co Ltd",
+[9][0x2d - 1] = "Neo Forza",
+[9][0x2e - 1] = "Lyontek Inc",
+[9][0x2f - 1] = "Shanghai Kuxin Microelectronics Ltd",
+[9][0x30 - 1] = "Shenzhen Larix Technology Co Ltd",
+[9][0x31 - 1] = "Qbit Semiconductor Ltd",
+[9][0x32 - 1] = "Insignis Technology Corporation",
+[9][0x33 - 1] = "Lanson Memory Co Ltd",
+[9][0x34 - 1] = "Shenzhen Superway Electronics Co Ltd",
+[9][0x35 - 1] = "Canaan-Creative Co Ltd",
+[9][0x36 - 1] = "Black Diamond Memory",
+[9][0x37 - 1] = "Shenzhen City Parker Baking Electronics",
+[9][0x38 - 1] = "Shenzhen Baihong Technology Co Ltd",
+[9][0x39 - 1] = "GEO Semiconductors",
+[9][0x3a - 1] = "OCPC",
+[9][0x3b - 1] = "Artery Technology Co Ltd",
+[9][0x3c - 1] = "Jinyu",
+[9][0x3d - 1] = "ShenzhenYing Chi Technology Development",
+[9][0x3e - 1] = "Shenzhen Pengcheng Xin Technology",
+[9][0x3f - 1] = "Pegasus Semiconductor (Shanghai) Co",
+[9][0x40 - 1] = "Mythic Inc",
+[9][0x41 - 1] = "Elmos Semiconductor AG",
+[9][0x42 - 1] = "Kllisre",
+[9][0x43 - 1] = "Shenzhen Winconway Technology",
+[9][0x44 - 1] = "Shenzhen Xingmem Technology Corp",
+[9][0x45 - 1] = "Gold Key Technology Co Ltd",
+[9][0x46 - 1] = "Habana Labs Ltd",
+[9][0x47 - 1] = "Hoodisk Electronics Co Ltd",
+[9][0x48 - 1] = "SemsoTai (SZ) Technology Co Ltd",
+[9][0x49 - 1] = "OM Nanotech Pvt. Ltd",
+[9][0x4a - 1] = "Shenzhen Zhifeng Weiye Technology",
+[9][0x4b - 1] = "Xinshirui (Shenzhen) Electronics Co",
+[9][0x4c - 1] = "Guangzhou Zhong Hao Tian Electronic",
+[9][0x4d - 1] = "Shenzhen Longsys Electronics Co Ltd",
+[9][0x4e - 1] = "Deciso B.V.",
+[9][0x4f - 1] = "Puya Semiconductor (Shenzhen)",
+[9][0x50 - 1] = "Shenzhen Veineda Technology Co Ltd",
+[9][0x51 - 1] = "Antec Memory",
+[9][0x52 - 1] = "Cortus SAS",
+[9][0x53 - 1] = "Dust Leopard",
+[9][0x54 - 1] = "MyWo AS",
+[9][0x55 - 1] = "J&A Information Inc",
+[9][0x56 - 1] = "Shenzhen JIEPEI Technology Co Ltd",
+[9][0x57 - 1] = "Heidelberg University",
+[9][0x58 - 1] = "Flexxon PTE Ltd",
+[9][0x59 - 1] = "Wiliot",
+[9][0x5a - 1] = "Raysun Electronics International Ltd",
+[9][0x5b - 1] = "Aquarius Production Company LLC",
+[9][0x5c - 1] = "MACNICA DHW LTDA",
+[9][0x5d - 1] = "Intelimem",
+[9][0x5e - 1] = "Zbit Semiconductor Inc",
+[9][0x5f - 1] = "Shenzhen Technology Co Ltd",
+[9][0x60 - 1] = "Signalchip",
+[9][0x61 - 1] = "Shenzen Recadata Storage Technology",
+[9][0x62 - 1] = "Hyundai Technology",
+[9][0x63 - 1] = "Shanghai Fudi Investment Development",
+[9][0x64 - 1] = "Aixi Technology",
+[9][0x65 - 1] = "Tecon MT",
+[9][0x66 - 1] = "Onda Electric Co Ltd",
+[9][0x67 - 1] = "Jinshen",
+[9][0x68 - 1] = "Kimtigo Semiconductor (HK) Limited",
+[9][0x69 - 1] = "IIT Madras",
+[9][0x6a - 1] = "Shenshan (Shenzhen) Electronic",
+[9][0x6b - 1] = "Hefei Core Storage Electronic Limited",
+[9][0x6c - 1] = "Colorful Technology Ltd",
+[9][0x6d - 1] = "Visenta (Xiamen) Technology Co Ltd",
+[9][0x6e - 1] = "Roa Logic BV",
+[9][0x6f - 1] = "NSITEXE Inc",
+[9][0x70 - 1] = "Hong Kong Hyunion Electronics",
+[9][0x71 - 1] = "ASK Technology Group Limited",
+[9][0x72 - 1] = "GIGA-BYTE Technology Co Ltd",
+[9][0x73 - 1] = "Terabyte Co Ltd",
+[9][0x74 - 1] = "Hyundai Inc",
+[9][0x75 - 1] = "EXCELERAM",
+[9][0x76 - 1] = "PsiKick",
+[9][0x77 - 1] = "Netac Technology Co Ltd",
+[9][0x78 - 1] = "PCCOOLER",
+[9][0x79 - 1] = "Jiangsu Huacun Electronic Technology",
+[9][0x7a - 1] = "Shenzhen Micro Innovation Industry",
+[9][0x7b - 1] = "Beijing Tongfang Microelectronics Co",
+[9][0x7c - 1] = "XZN Storage Technology",
+[9][0x7d - 1] = "ChipCraft Sp. z.o.o.",
+[9][0x7e - 1] = "ALLFLASH Technology Limited",
+[10][0x01 - 1] = "Foerd Technology Co Ltd",
+[10][0x02 - 1] = "KingSpec",
+[10][0x03 - 1] = "Codasip GmbH",
+[10][0x04 - 1] = "SL Link Co Ltd",
+[10][0x05 - 1] = "Shenzhen Kefu Technology Co Limited",
+[10][0x06 - 1] = "Shenzhen ZST Electronics Technology",
+[10][0x07 - 1] = "Kyokuto Electronic Inc",
+[10][0x08 - 1] = "Warrior Technology",
+[10][0x09 - 1] = "TRINAMIC Motion Control GmbH & Co",
+[10][0x0a - 1] = "PixelDisplay Inc",
+[10][0x0b - 1] = "Shenzhen Futian District Bo Yueda Elec",
+[10][0x0c - 1] = "Richtek Power",
+[10][0x0d - 1] = "Shenzhen LianTeng Electronics Co Ltd",
+[10][0x0e - 1] = "AITC Memory",
+[10][0x0f - 1] = "UNIC Memory Technology Co Ltd",
+[10][0x10 - 1] = "Shenzhen Huafeng Science Technology",
+[10][0x11 - 1] = "CXMT",
+[10][0x12 - 1] = "Guangzhou Xinyi Heng Computer Trading Firm",
+[10][0x13 - 1] = "SambaNova Systems",
+[10][0x14 - 1] = "V-GEN",
+[10][0x15 - 1] = "Jump Trading",
+[10][0x16 - 1] = "Ampere Computing",
+[10][0x17 - 1] = "Shenzhen Zhongshi Technology Co Ltd",
+[10][0x18 - 1] = "Shenzhen Zhongtian Bozhong Technology",
+[10][0x19 - 1] = "Tri-Tech International",
+[10][0x1a - 1] = "Silicon Intergrated Systems Corporation",
+[10][0x1b - 1] = "Shenzhen HongDingChen Information",
+[10][0x1c - 1] = "Plexton Holdings Limited",
+[10][0x1d - 1] = "AMS (Jiangsu Advanced Memory Semi)",
+[10][0x1e - 1] = "Wuhan Jing Tian Interconnected Tech Co",
+[10][0x1f - 1] = "Axia Memory Technology",
+[10][0x20 - 1] = "Chipset Technology Holding Limited",
+[10][0x21 - 1] = "Shenzhen Xinshida Technology Co Ltd",
+[10][0x22 - 1] = "Shenzhen Chuangshifeida Technology",
+[10][0x23 - 1] = "Guangzhou MiaoYuanJi Technology",
+[10][0x24 - 1] = "ADVAN Inc",
+[10][0x25 - 1] = "Shenzhen Qianhai Weishengda Electronic Commerce Company Ltd",
+[10][0x26 - 1] = "Guangzhou Guang Xie Cheng Trading",
+[10][0x27 - 1] = "StarRam International Co Ltd",
+[10][0x28 - 1] = "Shen Zhen XinShenHua Tech Co Ltd",
+[10][0x29 - 1] = "UltraMemory Inc",
+[10][0x2a - 1] = "New Coastline Global Tech Industry Co",
+[10][0x2b - 1] = "Sinker",
+[10][0x2c - 1] = "Diamond",
+[10][0x2d - 1] = "PUSKILL",
+[10][0x2e - 1] = "Guangzhou Hao Jia Ye Technology Co",
+[10][0x2f - 1] = "Ming Xin Limited",
+[10][0x30 - 1] = "Barefoot Networks",
+[10][0x31 - 1] = "Biwin Semiconductor (HK) Co Ltd",
+[10][0x32 - 1] = "UD INFO Corporation",
+[10][0x33 - 1] = "Trek Technology (S) PTE Ltd",
+[10][0x34 - 1] = "Xiamen Kingblaze Technology Co Ltd",
+[10][0x35 - 1] = "Shenzhen Lomica Technology Co Ltd",
+[10][0x36 - 1] = "Nuclei System Technology Co Ltd",
+[10][0x37 - 1] = "Wuhan Xun Zhan Electronic Technology",
+[10][0x38 - 1] = "Shenzhen Ingacom Semiconductor Ltd",
+[10][0x39 - 1] = "Zotac Technology Ltd",
+[10][0x3a - 1] = "Foxline",
+[10][0x3b - 1] = "Shenzhen Farasia Science Technology",
+[10][0x3c - 1] = "Efinix Inc",
+[10][0x3d - 1] = "Hua Nan San Xian Technology Co Ltd",
+[10][0x3e - 1] = "Goldtech Electronics Co Ltd",
+[10][0x3f - 1] = "Shanghai Han Rong Microelectronics Co",
+[10][0x40 - 1] = "Shenzhen Zhongguang Yunhe Trading",
+[10][0x41 - 1] = "Smart Shine(QingDao) Microelectronics",
+[10][0x42 - 1] = "Thermaltake Technology Co Ltd",
+[10][0x43 - 1] = "Shenzhen O'Yang Maile Technology Ltd",
+[10][0x44 - 1] = "UPMEM",
+[10][0x45 - 1] = "Chun Well Technology Holding Limited",
+[10][0x46 - 1] = "Astera Labs Inc",
+[10][0x47 - 1] = "Winconway",
+[10][0x48 - 1] = "Advantech Co Ltd",
+[10][0x49 - 1] = "Chengdu Fengcai Electronic Technology",
+[10][0x4a - 1] = "The Boeing Company",
+[10][0x4b - 1] = "Blaize Inc",
+[10][0x4c - 1] = "Ramonster Technology Co Ltd",
+[10][0x4d - 1] = "Wuhan Naonongmai Technology Co Ltd",
+[10][0x4e - 1] = "Shenzhen Hui ShingTong Technology",
+[10][0x4f - 1] = "Yourlyon",
+[10][0x50 - 1] = "Fabu Technology",
+[10][0x51 - 1] = "Shenzhen Yikesheng Technology Co Ltd",
+[10][0x52 - 1] = "NOR-MEM",
+[10][0x53 - 1] = "Cervoz Co Ltd",
+[10][0x54 - 1] = "Bitmain Technologies Inc.",
+[10][0x55 - 1] = "Facebook Inc",
+[10][0x56 - 1] = "Shenzhen Longsys Electronics Co Ltd",
+[10][0x57 - 1] = "Guangzhou Siye Electronic Technology",
+[10][0x58 - 1] = "Silergy",
+[10][0x59 - 1] = "Adamway",
+[10][0x5a - 1] = "PZG",
+[10][0x5b - 1] = "Shenzhen King Power Electronics",
+[10][0x5c - 1] = "Guangzhou ZiaoFu Tranding Co Ltd",
+[10][0x5d - 1] = "Shenzhen SKIHOTAR Semiconductor",
+[10][0x5e - 1] = "PulseRain Technology",
+[10][0x5f - 1] = "Seeker Technology Limited",
+[10][0x60 - 1] = "Shenzhen OSCOO Tech Co Ltd",
+[10][0x61 - 1] = "Shenzhen Yze Technology Co Ltd",
+[10][0x62 - 1] = "Shenzhen Jieshuo Electronic Commerce",
+[10][0x63 - 1] = "Gazda",
+[10][0x64 - 1] = "Hua Wei Technology Co Ltd",
+[10][0x65 - 1] = "Esperanto Technologies",
+[10][0x66 - 1] = "JinSheng Electronic (Shenzhen) Co Ltd",
+[10][0x67 - 1] = "Shenzhen Shi Bolunshuai Technology",
+[10][0x68 - 1] = "Shanghai Rei Zuan Information Tech",
+[10][0x69 - 1] = "Fraunhofer IIS",
+[10][0x6a - 1] = "Kandou Bus SA",
+[10][0x6b - 1] = "Acer",
+[10][0x6c - 1] = "Artmem Technology Co Ltd",
+[10][0x6d - 1] = "Gstar Semiconductor Co Ltd",
+[10][0x6e - 1] = "ShineDisk",
+[10][0x6f - 1] = "Shenzhen CHN Technology Co Ltd",
+[10][0x70 - 1] = "UnionChip Semiconductor Co Ltd",
+[10][0x71 - 1] = "Tanbassh",
+[10][0x72 - 1] = "Shenzhen Tianyu Jieyun Intl Logistics",
+[10][0x73 - 1] = "MCLogic Inc",
+[10][0x74 - 1] = "Eorex Corporation",
+[10][0x75 - 1] = "Arm Technology (China) Co Ltd",
+[10][0x76 - 1] = "Lexar Co Limited",
+[10][0x77 - 1] = "QinetiQ Group plc",
+[10][0x78 - 1] = "Exascend",
+[10][0x79 - 1] = "Hong Kong Hyunion Electronics Co Ltd",
+[10][0x7a - 1] = "Shenzhen Banghong Electronics Co Ltd",
+[10][0x7b - 1] = "MBit Wireless Inc",
+[10][0x7c - 1] = "Hex Five Security Inc",
+[10][0x7d - 1] = "ShenZhen Juhor Precision Tech Co Ltd",
+[10][0x7e - 1] = "Shenzhen Reeinno Technology Co Ltd",
+[11][0x01 - 1] = "ABIT Electronics (Shenzhen) Co Ltd",
+[11][0x02 - 1] = "Semidrive",
+[11][0x03 - 1] = "MyTek Electronics Corp",
+[11][0x04 - 1] = "Wxilicon Technology Co Ltd",
+[11][0x05 - 1] = "Shenzhen Meixin Electronics Ltd",
+[11][0x06 - 1] = "Ghost Wolf",
+[11][0x07 - 1] = "LiSion Technologies Inc",
+[11][0x08 - 1] = "Power Active Co Ltd",
+[11][0x09 - 1] = "Pioneer High Fidelity Taiwan Co. Ltd",
+[11][0x0a - 1] = "LuoSilk",
+[11][0x0b - 1] = "Shenzhen Chuangshifeida Technology",
+[11][0x0c - 1] = "Black Sesame Technologies Inc",
+[11][0x0d - 1] = "Jiangsu Xinsheng Intelligent Technology",
+[11][0x0e - 1] = "MLOONG",
+[11][0x0f - 1] = "Quadratica LLC",
+[11][0x10 - 1] = "Anpec Electronics",
+[11][0x11 - 1] = "Xi'an Morebeck Semiconductor Tech Co",
+[11][0x12 - 1] = "Kingbank Technology Co Ltd",
+[11][0x13 - 1] = "ITRenew Inc",
+[11][0x14 - 1] = "Shenzhen Eaget Innovation Tech Ltd",
+[11][0x15 - 1] = "Jazer",
+[11][0x16 - 1] = "Xiamen Semiconductor Investment Group",
+[11][0x17 - 1] = "Guangzhou Longdao Network Tech Co",
+[11][0x18 - 1] = "Shenzhen Futian SEC Electronic Market",
+[11][0x19 - 1] = "Allegro Microsystems LLC",
+[11][0x1a - 1] = "Hunan RunCore Innovation Technology",
+[11][0x1b - 1] = "C-Corsa Technology",
+[11][0x1c - 1] = "Zhuhai Chuangfeixin Technology Co Ltd",
+[11][0x1d - 1] = "Beijing InnoMem Technologies Co Ltd",
+[11][0x1e - 1] = "YooTin",
+[11][0x1f - 1] = "Shenzhen Pengxiong Technology Co Ltd",
+[11][0x20 - 1] = "Dongguan Yingbang Commercial Trading Co",
+[11][0x21 - 1] = "Shenzhen Ronisys Electronics Co Ltd",
+[11][0x22 - 1] = "Hongkong Xinlan Guangke Co Ltd",
+[11][0x23 - 1] = "Apex Microelectronics Co Ltd",
+[11][0x24 - 1] = "Beijing Hongda Jinming Technology Co Ltd",
+[11][0x25 - 1] = "Ling Rui Technology (Shenzhen) Co Ltd",
+[11][0x26 - 1] = "Hongkong Hyunion Electronics Co Ltd",
+[11][0x27 - 1] = "Starsystems Inc",
+[11][0x28 - 1] = "Shenzhen Yingjiaxun Industrial Co Ltd",
+[11][0x29 - 1] = "Dongguan Crown Code Electronic Commerce",
+[11][0x2a - 1] = "Monolithic Power Systems Inc",
+[11][0x2b - 1] = "WuHan SenNaiBo E-Commerce Co Ltd",
+[11][0x2c - 1] = "Hangzhou Hikstorage Technology Co",
+[11][0x2d - 1] = "Shenzhen Goodix Technology Co Ltd",
+[11][0x2e - 1] = "Aigo Electronic Technology Co Ltd",
+[11][0x2f - 1] = "Hefei Konsemi Storage Technology Co Ltd",
+[11][0x30 - 1] = "Cactus Technologies Limited",
+[11][0x31 - 1] = "DSIN",
+[11][0x32 - 1] = "Blu Wireless Technology",
+[11][0x33 - 1] = "Nanjing UCUN Technology Inc",
+[11][0x34 - 1] = "Acacia Communications",
+[11][0x35 - 1] = "Beijinjinshengyihe Technology Co Ltd",
+[11][0x36 - 1] = "Zyzyx",
+[11][0x37 - 1] = "T-HEAD Semiconductor Co Ltd",
+[11][0x38 - 1] = "Shenzhen Hystou Technology Co Ltd",
+[11][0x39 - 1] = "Syzexion",
+[11][0x3a - 1] = "Kembona",
+[11][0x3b - 1] = "Qingdao Thunderobot Technology Co Ltd",
+[11][0x3c - 1] = "Morse Micro",
+[11][0x3d - 1] = "Shenzhen Envida Technology Co Ltd",
+[11][0x3e - 1] = "UDStore Solution Limited",
+[11][0x3f - 1] = "Shunlie",
+[11][0x40 - 1] = "Shenzhen Xin Hong Rui Tech Ltd",
+[11][0x41 - 1] = "Shenzhen Yze Technology Co Ltd",
+[11][0x42 - 1] = "Shenzhen Huang Pu He Xin Technology",
+[11][0x43 - 1] = "Xiamen Pengpai Microelectronics Co Ltd",
+[11][0x44 - 1] = "JISHUN",
+[11][0x45 - 1] = "Shenzhen WODPOSIT Technology Co",
+[11][0x46 - 1] = "Unistar",
+[11][0x47 - 1] = "UNICORE Electronic (Suzhou) Co Ltd",
+[11][0x48 - 1] = "Axonne Inc",
+[11][0x49 - 1] = "Shenzhen SOVERECA Technology Co",
+[11][0x4a - 1] = "Dire Wolf",
+[11][0x4b - 1] = "Whampoa Core Technology Co Ltd",
+[11][0x4c - 1] = "CSI Halbleiter GmbH",
+[11][0x4d - 1] = "ONE Semiconductor",
+[11][0x4e - 1] = "SimpleMachines Inc",
+[11][0x4f - 1] = "Shenzhen Chengyi Qingdian Electronic",
+[11][0x50 - 1] = "Shenzhen Xinlianxin Network Technology",
+[11][0x51 - 1] = "Vayyar Imaging Ltd",
+[11][0x52 - 1] = "Paisen Network Technology Co Ltd",
+[11][0x53 - 1] = "Shenzhen Fengwensi Technology Co Ltd",
+[11][0x54 - 1] = "Caplink Technology Limited",
+[11][0x55 - 1] = "JJT Solution Co Ltd",
+[11][0x56 - 1] = "HOSIN Global Electronics Co Ltd",
+[11][0x57 - 1] = "Shenzhen KingDisk Century Technology",
+[11][0x58 - 1] = "SOYO",
+[11][0x59 - 1] = "DIT Technology Co Ltd",
+[11][0x5a - 1] = "iFound",
+[11][0x5b - 1] = "Aril Computer Company",
+[11][0x5c - 1] = "ASUS",
+[11][0x5d - 1] = "Shenzhen Ruiyingtong Technology Co",
+[11][0x5e - 1] = "HANA Micron",
+[11][0x5f - 1] = "RANSOR",
+[11][0x60 - 1] = "Axiado Corporation",
+[11][0x61 - 1] = "Tesla Corporation",
+[11][0x62 - 1] = "Pingtouge (Shanghai) Semiconductor Co",
+[11][0x63 - 1] = "S3Plus Technologies SA",
+[11][0x64 - 1] = "Integrated Silicon Solution Israel Ltd",
+[11][0x65 - 1] = "GreenWaves Technologies",
+[11][0x66 - 1] = "NUVIA Inc",
+[11][0x67 - 1] = "Guangzhou Shuvrwine Technology Co",
+[11][0x68 - 1] = "Shenzhen Hangshun Chip Technology",
+[11][0x69 - 1] = "Chengboliwei Electronic Business",
+[11][0x6a - 1] = "Kowin Technology HK Limited",
+[11][0x6b - 1] = "Euronet Technology Inc",
+[11][0x6c - 1] = "SCY",
+[11][0x6d - 1] = "Shenzhen Xinhongyusheng Electrical",
+[11][0x6e - 1] = "PICOCOM",
+[11][0x6f - 1] = "Shenzhen Toooogo Memory Technology",
+[11][0x70 - 1] = "VLSI Solution",
+[11][0x71 - 1] = "Costar Electronics Inc",
+[11][0x72 - 1] = "Shenzhen Huatop Technology Co Ltd",
+[11][0x73 - 1] = "Inspur Electronic Information Industry",
+[11][0x74 - 1] = "Shenzhen Boyuan Computer Technology",
+[11][0x75 - 1] = "Beijing Welldisk Electronics Co Ltd",
+[11][0x76 - 1] = "Suzhou EP Semicon Co Ltd",
+[11][0x77 - 1] = "Zhejiang Dahua Memory Technology",
+[11][0x78 - 1] = "Virtu Financial",
+[11][0x79 - 1] = "Datotek International Co Ltd",
+[11][0x7a - 1] = "Telecom and Microelectronics Industries",
+[11][0x7b - 1] = "Echow Technology Ltd",
+[11][0x7c - 1] = "APEX-INFO",
+[11][0x7d - 1] = "Yingpark",
+[11][0x7e - 1] = "Shenzhen Bigway Tech Co Ltd",
+[12][0x01 - 1] = "Beijing Haawking Technology Co Ltd",
+[12][0x02 - 1] = "Open HW Group",
+[12][0x03 - 1] = "JHICC",
+[12][0x04 - 1] = "ncoder AG",
+[12][0x05 - 1] = "ThinkTech Information Technology Co",
+[12][0x06 - 1] = "Shenzhen Chixingzhe Technology Co Ltd",
+[12][0x07 - 1] = "Biao Ram Technology Co Ltd",
+[12][0x08 - 1] = "Shenzhen Kaizhuoyue Electronics Co Ltd",
+[12][0x09 - 1] = "Shenzhen YC Storage Technology Co Ltd",
+[12][0x0a - 1] = "Shenzhen Chixingzhe Technology Co",
+[12][0x0b - 1] = "Wink Semiconductor (Shenzhen) Co Ltd",
+[12][0x0c - 1] = "AISTOR",
+[12][0x0d - 1] = "Palma Ceia SemiDesign",
+[12][0x0e - 1] = "EM Microelectronic-Marin SA",
+[12][0x0f - 1] = "Shenzhen Monarch Memory Technology",
+[12][0x10 - 1] = "Reliance Memory Inc",
+[12][0x11 - 1] = "Jesis",
+[12][0x12 - 1] = "Espressif Systems (Shanghai) Co Ltd",
+[12][0x13 - 1] = "Shenzhen Sati Smart Technology Co Ltd",
+[12][0x14 - 1] = "NeuMem Co Ltd",
+[12][0x15 - 1] = "Lifelong",
+[12][0x16 - 1] = "Beijing Oitech Technology Co Ltd",
+[12][0x17 - 1] = "Groupe LDLC",
+[12][0x18 - 1] = "Semidynamics Technology Services SLU",
+[12][0x19 - 1] = "swordbill",
+[12][0x1a - 1] = "YIREN",
+[12][0x1b - 1] = "Shenzhen Yinxiang Technology Co Ltd",
+[12][0x1c - 1] = "PoweV Electronic Technology Co Ltd",
+[12][0x1d - 1] = "LEORICE",
+[12][0x1e - 1] = "Waymo LLC",
+[12][0x1f - 1] = "Ventana Micro Systems",
+[12][0x20 - 1] = "Hefei Guangxin Microelectronics Co Ltd",
+[12][0x21 - 1] = "Shenzhen Sooner Industrial Co Ltd",
+[12][0x22 - 1] = "Horizon Robotics",
+[12][0x23 - 1] = "Tangem AG",
+[12][0x24 - 1] = "FuturePath Technology (Shenzhen) Co",
+[12][0x25 - 1] = "RC Module",
+[12][0x26 - 1] = "Timetec International Inc",
+[12][0x27 - 1] = "ICMAX Technologies Co Limited",
+[12][0x28 - 1] = "Lynxi Technologies Ltd Co",
+[12][0x29 - 1] = "Guangzhou Taisupanke Computer Equipment",
+[12][0x2a - 1] = "Ceremorphic Inc",
+[12][0x2b - 1] = "Biwin Storage Technology Co Ltd",
+[12][0x2c - 1] = "Beijing ESWIN Computing Technology",
+[12][0x2d - 1] = "WeForce Co Ltd",
+[12][0x2e - 1] = "Shenzhen Fanxiang Information Technology",
+[12][0x2f - 1] = "Unisoc",
+[12][0x30 - 1] = "YingChu",
+[12][0x31 - 1] = "GUANCUN",
+[12][0x32 - 1] = "IPASON",
+[12][0x33 - 1] = "Ayar Labs",
+[12][0x34 - 1] = "Amazon",
+[12][0x35 - 1] = "Shenzhen Xinxinshun Technology Co",
+[12][0x36 - 1] = "Galois Inc",
+[12][0x37 - 1] = "Ubilite Inc",
+[12][0x38 - 1] = "Shenzhen Quanxing Technology Co Ltd",
+[12][0x39 - 1] = "Group RZX Technology LTDA",
+[12][0x3a - 1] = "Yottac Technology (XI'AN) Cooperation",
+[12][0x3b - 1] = "Shenzhen RuiRen Technology Co Ltd",
+[12][0x3c - 1] = "Group Star Technology Co Ltd",
+[12][0x3d - 1] = "RWA (Hong Kong) Ltd",
+[12][0x3e - 1] = "Genesys Logic Inc",
+[12][0x3f - 1] = "T3 Robotics Inc.",
+[12][0x40 - 1] = "Biostar Microtech International Corp",
+[12][0x41 - 1] = "Shenzhen SXmicro Technology Co Ltd",
+[12][0x42 - 1] = "Shanghai Yili Computer Technology Co",
+[12][0x43 - 1] = "Zhixin Semicoducotor Co Ltd",
+[12][0x44 - 1] = "uFound",
+[12][0x45 - 1] = "Aigo Data Security Technology Co. Ltd",
+[12][0x46 - 1] = ".GXore Technologies",
+[12][0x47 - 1] = "Shenzhen Pradeon Intelligent Technology",
+[12][0x48 - 1] = "Power LSI",
+[12][0x49 - 1] = "PRIME",
+[12][0x4a - 1] = "Shenzhen Juyang Innovative Technology",
+[12][0x4b - 1] = "CERVO",
+[12][0x4c - 1] = "SiEngine Technology Co., Ltd.",
+[12][0x4d - 1] = "Beijing Unigroup Tsingteng MicroSystem",
+[12][0x4e - 1] = "Brainsao GmbH",
+[12][0x4f - 1] = "Credo Technology Group Ltd",
+[12][0x50 - 1] = "Shanghai Biren Technology Co Ltd",
+[12][0x51 - 1] = "Nucleu Semiconductor",
+[12][0x52 - 1] = "Shenzhen Guangshuo Electronics Co Ltd",
+[12][0x53 - 1] = "ZhongsihangTechnology Co Ltd",
+[12][0x54 - 1] = "Suzhou Mainshine Electronic Co Ltd.",
+[12][0x55 - 1] = "Guangzhou Riss Electronic Technology",
+[12][0x56 - 1] = "Shenzhen Cloud Security Storage Co",
+[12][0x57 - 1] = "ROG",
+[12][0x58 - 1] = "Perceive",
+[12][0x59 - 1] = "e-peas",
+[12][0x5a - 1] = "Fraunhofer IPMS",
+[12][0x5b - 1] = "Shenzhen Daxinlang Electronic Tech Co",
+[12][0x5c - 1] = "Abacus Peripherals Private Limited",
+[12][0x5d - 1] = "OLOy Technology",
+[12][0x5e - 1] = "Wuhan P&S Semiconductor Co Ltd",
+[12][0x5f - 1] = "Sitrus Technology",
+[12][0x60 - 1] = "AnHui Conner Storage Co Ltd",
+[12][0x61 - 1] = "Rochester Electronics",
+[12][0x62 - 1] = "Wuxi Petabyte Technologies Co Ltd",
+[12][0x63 - 1] = "Star Memory",
+[12][0x64 - 1] = "Agile Memory Technology Co Ltd",
+[12][0x65 - 1] = "MEJEC",
+[12][0x66 - 1] = "Rockchip Electronics Co Ltd",
+[12][0x67 - 1] = "Dongguan Guanma e-commerce Co Ltd",
+[12][0x68 - 1] = "Rayson Hi-Tech (SZ) Limited",
+[12][0x69 - 1] = "MINRES Technologies GmbH",
+[12][0x6a - 1] = "Himax Technologies Inc",
+[12][0x6b - 1] = "Shenzhen Cwinner Technology Co Ltd",
+[12][0x6c - 1] = "Tecmiyo",
+[12][0x6d - 1] = "Shenzhen Suhuicun Technology Co Ltd",
+[12][0x6e - 1] = "Vickter Electronics Co. Ltd.",
+[12][0x6f - 1] = "lowRISC",
+[12][0x70 - 1] = "EXEGate FZE",
+[12][0x71 - 1] = "Shenzhen 9 Chapter Technologies Co",
+[12][0x72 - 1] = "Addlink",
+[12][0x73 - 1] = "Starsway",
+[12][0x74 - 1] = "Pensando Systems Inc.",
+[12][0x75 - 1] = "AirDisk",
+[12][0x76 - 1] = "Shenzhen Speedmobile Technology Co",
+[12][0x77 - 1] = "PEZY Computing",
+[12][0x78 - 1] = "Extreme Engineering Solutions Inc",
+[12][0x79 - 1] = "Shangxin Technology Co Ltd",
+[12][0x7a - 1] = "Shanghai Zhaoxin Semiconductor Co",
+[12][0x7b - 1] = "Xsight Labs Ltd",
+[12][0x7c - 1] = "Hangzhou Hikstorage Technology Co",
+[12][0x7d - 1] = "Dell Technologies",
+[12][0x7e - 1] = "Guangdong StarFive Technology Co",
+[13][0x01 - 1] = "TECOTON",
+[13][0x02 - 1] = "Abko Co Ltd",
+[13][0x03 - 1] = "Shenzhen Feisrike Technology Co Ltd",
+[13][0x04 - 1] = "Shenzhen Sunhome Electronics Co Ltd",
+[13][0x05 - 1] = "Global Mixed-mode Technology Inc",
+[13][0x06 - 1] = "Shenzhen Weien Electronics Co. Ltd.",
+[13][0x07 - 1] = "Shenzhen Cooyes Technology Co Ltd",
+[13][0x08 - 1] = "Keymos Electronics Co., Limited",
+[13][0x09 - 1] = "E-Rockic Technology Company Limited",
+[13][0x0a - 1] = "Aerospace Science Memory Shenzhen",
+[13][0x0b - 1] = "Shenzhen Quanji Technology Co Ltd",
+[13][0x0c - 1] = "Dukosi",
+[13][0x0d - 1] = "Maxell Corporation of America",
+[13][0x0e - 1] = "Shenshen Xinxintao Electronics Co Ltd",
+[13][0x0f - 1] = "Zhuhai Sanxia Semiconductor Co Ltd",
+[13][0x10 - 1] = "Groq Inc",
+[13][0x11 - 1] = "AstraTek",
+[13][0x12 - 1] = "Shenzhen Xinyuze Technology Co Ltd",
+[13][0x13 - 1] = "All Bit Semiconductor",
+[13][0x14 - 1] = "ACFlow",
+[13][0x15 - 1] = "Shenzhen Sipeed Technology Co Ltd",
+[13][0x16 - 1] = "Linzhi Hong Kong Co Limited",
+[13][0x17 - 1] = "Supreme Wise Limited",
+[13][0x18 - 1] = "Blue Cheetah Analog Design Inc",
+[13][0x19 - 1] = "Hefei Laiku Technology Co Ltd",
+[13][0x1a - 1] = "Zord",
+[13][0x1b - 1] = "SBO Hearing A/S",
+[13][0x1c - 1] = "Regent Sharp International Limited",
+[13][0x1d - 1] = "Permanent Potential Limited",
+[13][0x1e - 1] = "Creative World International Limited",
+[13][0x1f - 1] = "Base Creation International Limited",
+[13][0x20 - 1] = "Shenzhen Zhixin Chuanglian Technology",
+[13][0x21 - 1] = "Protected Logic Corporation",
+[13][0x22 - 1] = "Sabrent",
+[13][0x23 - 1] = "Union Memory",
+[13][0x24 - 1] = "NEUCHIPS Corporation",
+[13][0x25 - 1] = "Ingenic Semiconductor Co Ltd",
+[13][0x26 - 1] = "SiPearl",
+[13][0x27 - 1] = "Shenzhen Actseno Information Technology",
+[13][0x28 - 1] = "RIVAI Technologies (Shenzhen) Co Ltd",
+[13][0x29 - 1] = "Shenzhen Sunny Technology Co Ltd",
+[13][0x2a - 1] = "Cott Electronics Ltd",
+[13][0x2b - 1] = "Shanghai Synsense Technologies Co Ltd",
+[13][0x2c - 1] = "Shenzhen Jintang Fuming Optoelectronics",
+[13][0x2d - 1] = "CloudBEAR LLC",
+[13][0x2e - 1] = "Emzior, LLC",
+[13][0x2f - 1] = "Ehiway Microelectronic Science Tech Co",
+[13][0x30 - 1] = "UNIM Innovation Technology (Wu XI)",
+[13][0x31 - 1] = "GDRAMARS",
+[13][0x32 - 1] = "Meminsights Technology",
+[13][0x33 - 1] = "Zhuzhou Hongda Electronics Corp Ltd",
+[13][0x34 - 1] = "Luminous Computing Inc",
+[13][0x35 - 1] = "PROXMEM",
+[13][0x36 - 1] = "Draper Labs",
+[13][0x37 - 1] = "ORICO Technologies Co. Ltd.",
+[13][0x38 - 1] = "Space Exploration Technologies Corp",
+[13][0x39 - 1] = "AONDEVICES Inc",
+[13][0x3a - 1] = "Shenzhen Netforward Micro Electronic",
+[13][0x3b - 1] = "Syntacore Ltd",
+[13][0x3c - 1] = "Shenzhen Secmem Microelectronics Co",
+[13][0x3d - 1] = "ONiO As",
+[13][0x3e - 1] = "Shenzhen Peladn Technology Co Ltd",
+[13][0x3f - 1] = "O-Cubes Shanghai Microelectronics",
+[13][0x40 - 1] = "ASTC",
+[13][0x41 - 1] = "UMIS",
+[13][0x42 - 1] = "Paradromics",
+[13][0x43 - 1] = "Sinh Micro Co Ltd",
+[13][0x44 - 1] = "Metorage Semiconductor Technology Co",
+[13][0x45 - 1] = "Aeva Inc",
+[13][0x46 - 1] = "HongKong Hyunion Electronics Co Ltd",
+[13][0x47 - 1] = "China Flash Co Ltd",
+[13][0x48 - 1] = "Sunplus Technology Co Ltd",
+[13][0x49 - 1] = "Idaho Scientific",
+[13][0x4a - 1] = "Suzhou SF Micro Electronics Co Ltd",
+[13][0x4b - 1] = "IMEX Cap AG",
+[13][0x4c - 1] = "Fitipower Integrated Technology Co Ltd",
+[13][0x4d - 1] = "ShenzhenWooacme Technology Co Ltd",
+[13][0x4e - 1] = "KeepData Original Chips",
+[13][0x4f - 1] = "Rivos Inc",
+[13][0x50 - 1] = "Big Innovation Company Limited",
+[13][0x51 - 1] = "Wuhan YuXin Semiconductor Co Ltd",
+[13][0x52 - 1] = "United Memory Technology (Jiangsu)",
+[13][0x53 - 1] = "PQShield Ltd",
+[13][0x54 - 1] = "ArchiTek Corporation",
+[13][0x55 - 1] = "ShenZhen AZW Technology Co Ltd",
+[13][0x56 - 1] = "Hengchi Zhixin (Dongguan) Technology",
+[13][0x57 - 1] = "Eggtronic Engineering Spa",
+[13][0x58 - 1] = "Fusontai Technology",
+[13][0x59 - 1] = "PULP Platform",
+[13][0x5a - 1] = "Koitek Electronic Technology (Shenzhen) Co",
+[13][0x5b - 1] = "Shenzhen Jiteng Network Technology Co",
+[13][0x5c - 1] = "Aviva Links Inc",
+[13][0x5d - 1] = "Trilinear Technologies Inc",
+[13][0x5e - 1] = "Shenzhen Developer Microelectronics Co",
+[13][0x5f - 1] = "Guangdong OPPO Mobile Telecommunication",
+[13][0x60 - 1] = "Akeana",
+[13][0x61 - 1] = "Lyczar",
+[13][0x62 - 1] = "Shenzhen Qiji Technology Co Ltd",
+[13][0x63 - 1] = "Shenzhen Shangzhaoyuan Technology",
+[13][0x64 - 1] = "Han Stor",
+[13][0x65 - 1] = "China Micro Semicon Co., Ltd.",
+[13][0x66 - 1] = "Shenzhen Zhuqin Technology Co Ltd",
+[13][0x67 - 1] = "Shanghai Ningyuan Electronic Technology",
+[13][0x68 - 1] = "Auradine",
+[13][0x69 - 1] = "Suzhou Yishuo Electronics Co Ltd",
+[13][0x6a - 1] = "Faurecia Clarion Electronics",
+[13][0x6b - 1] = "SiMa Technologies",
+[13][0x6c - 1] = "CFD Sales Inc",
+[13][0x6d - 1] = "Suzhou Comay Information Co Ltd",
+[13][0x6e - 1] = "Yentek",
+[13][0x6f - 1] = "Qorvo Inc",
+[13][0x70 - 1] = "Shenzhen Youzhi Computer Technology",
+[13][0x71 - 1] = "Sychw Technology (Shenzhen) Co Ltd",
+[13][0x72 - 1] = "MK Founder Technology Co Ltd",
+[13][0x73 - 1] = "Siliconwaves Technologies Co Ltd",
+[13][0x74 - 1] = "Hongkong Hyunion Electronics Co Ltd",
+[13][0x75 - 1] = "Shenzhen Xinxinzhitao Electronics Business",
+[13][0x76 - 1] = "Shenzhen HenQi Electronic Commerce Co",
+[13][0x77 - 1] = "Shenzhen Jingyi Technology Co Ltd",
+[13][0x78 - 1] = "Xiaohua Semiconductor Co. Ltd.",
+[13][0x79 - 1] = "Shenzhen Dalu Semiconductor Technology",
+[13][0x7a - 1] = "Shenzhen Ninespeed Electronics Co Ltd",
+[13][0x7b - 1] = "ICYC Semiconductor Co Ltd",
+[13][0x7c - 1] = "Shenzhen Jaguar Microsystems Co Ltd",
+[13][0x7d - 1] = "Beijing EC-Founder Co Ltd",
+[13][0x7e - 1] = "Shenzhen Taike Industrial Automation Co",
+[14][0x01 - 1] = "Kalray SA",
+[14][0x02 - 1] = "Shanghai Iluvatar CoreX Semiconductor Co",
+[14][0x03 - 1] = "Fungible Inc",
+[14][0x04 - 1] = "Song Industria E Comercio de Eletronicos",
+[14][0x05 - 1] = "DreamBig Semiconductor Inc",
+[14][0x06 - 1] = "ChampTek Electronics Corp",
+[14][0x07 - 1] = "Fusontai Technology",
+[14][0x08 - 1] = "Endress Hauser AG",
+[14][0x09 - 1] = "altec ComputerSysteme GmbH",
+[14][0x0a - 1] = "UltraRISC Technology (Shanghai) Co Ltd",
+[14][0x0b - 1] = "Shenzhen Jing Da Kang Technology Co Ltd",
+[14][0x0c - 1] = "Hangzhou Hongjun Microelectronics Co Ltd",
+/* EOF */

+ 216 - 0
swd_probe/model/chip.ply

@@ -0,0 +1,216 @@
+ply
+format ascii 1.0
+comment Created by Blender 3.3.1 - www.blender.org
+element vertex 136
+property float x
+property float y
+property float z
+element face 70
+property list uchar uint vertex_indices
+end_header
+1.000000 1.000000 0.152153
+-1.000000 1.000000 0.152153
+-1.000000 -1.000000 0.152153
+1.000000 -1.000000 0.152153
+1.000000 -1.000000 -0.185787
+-1.000000 -1.000000 -0.185787
+-1.000000 1.000000 -0.185787
+1.000000 1.000000 -0.185787
+-1.000043 -0.785071 -0.015780
+-1.155724 -0.785071 -0.015780
+-1.155724 -0.918718 -0.015780
+-1.000043 -0.918718 -0.015780
+-1.155724 -0.785071 0.127052
+-1.000043 -0.785071 0.127052
+-1.000043 -0.918718 0.127052
+-1.155724 -0.918718 0.127052
+-1.234192 -0.918846 -0.087021
+-1.234397 -0.785201 -0.086336
+-1.235319 -0.784943 -0.229143
+-1.235114 -0.918588 -0.229828
+-1.388133 -0.919573 -0.078673
+-1.389056 -0.919314 -0.221479
+-1.389261 -0.785669 -0.220795
+-1.388338 -0.785927 -0.077988
+-1.000043 -0.219627 -0.015780
+-1.155724 -0.219627 -0.015780
+-1.155724 -0.353273 -0.015780
+-1.000043 -0.353273 -0.015780
+-1.155724 -0.219627 0.127052
+-1.000043 -0.219627 0.127052
+-1.000043 -0.353273 0.127052
+-1.155724 -0.353273 0.127052
+-1.234192 -0.353402 -0.087021
+-1.234397 -0.219756 -0.086336
+-1.235319 -0.219498 -0.229143
+-1.235114 -0.353143 -0.229828
+-1.388133 -0.354128 -0.078673
+-1.389056 -0.353870 -0.221479
+-1.389261 -0.220224 -0.220795
+-1.388338 -0.220482 -0.077988
+-1.000043 0.345818 -0.015780
+-1.155724 0.345818 -0.015780
+-1.155724 0.212172 -0.015780
+-1.000043 0.212172 -0.015780
+-1.155724 0.345818 0.127052
+-1.000043 0.345818 0.127052
+-1.000043 0.212172 0.127052
+-1.155724 0.212172 0.127052
+-1.234192 0.212043 -0.087021
+-1.234397 0.345689 -0.086336
+-1.235319 0.345947 -0.229143
+-1.235114 0.212301 -0.229828
+-1.388133 0.211317 -0.078673
+-1.389056 0.211575 -0.221479
+-1.389261 0.345221 -0.220795
+-1.388338 0.344962 -0.077988
+-1.000043 0.911263 -0.015780
+-1.155724 0.911263 -0.015780
+-1.155724 0.777617 -0.015780
+-1.000043 0.777617 -0.015780
+-1.155724 0.911263 0.127052
+-1.000043 0.911263 0.127052
+-1.000043 0.777617 0.127052
+-1.155724 0.777617 0.127052
+-1.234192 0.777488 -0.087021
+-1.234397 0.911133 -0.086336
+-1.235319 0.911392 -0.229143
+-1.235114 0.777746 -0.229828
+-1.388133 0.776762 -0.078673
+-1.389056 0.777020 -0.221479
+-1.389261 0.910665 -0.220795
+-1.388338 0.910407 -0.077988
+1.000043 -0.785071 -0.015780
+1.000043 -0.918718 -0.015780
+1.155723 -0.918718 -0.015780
+1.155723 -0.785071 -0.015780
+1.155723 -0.785071 0.127052
+1.155723 -0.918718 0.127052
+1.000043 -0.918718 0.127052
+1.000043 -0.785071 0.127052
+1.234397 -0.785201 -0.086336
+1.234192 -0.918846 -0.087021
+1.235114 -0.918588 -0.229828
+1.235319 -0.784943 -0.229143
+1.388133 -0.919573 -0.078673
+1.388338 -0.785927 -0.077988
+1.389260 -0.785669 -0.220795
+1.389056 -0.919314 -0.221479
+1.000043 -0.219627 -0.015780
+1.000043 -0.353273 -0.015780
+1.155723 -0.353273 -0.015780
+1.155723 -0.219627 -0.015780
+1.155723 -0.219627 0.127052
+1.155723 -0.353273 0.127052
+1.000043 -0.353273 0.127052
+1.000043 -0.219627 0.127052
+1.234397 -0.219756 -0.086336
+1.234192 -0.353402 -0.087021
+1.235114 -0.353143 -0.229828
+1.235319 -0.219498 -0.229143
+1.388133 -0.354128 -0.078673
+1.388338 -0.220482 -0.077988
+1.389260 -0.220224 -0.220795
+1.389056 -0.353870 -0.221479
+1.000043 0.345818 -0.015780
+1.000043 0.212172 -0.015780
+1.155723 0.212172 -0.015780
+1.155723 0.345818 -0.015780
+1.155723 0.345818 0.127052
+1.155723 0.212172 0.127052
+1.000043 0.212172 0.127052
+1.000043 0.345818 0.127052
+1.234397 0.345689 -0.086336
+1.234192 0.212043 -0.087021
+1.235114 0.212301 -0.229828
+1.235319 0.345947 -0.229143
+1.388133 0.211317 -0.078673
+1.388338 0.344962 -0.077988
+1.389260 0.345221 -0.220795
+1.389056 0.211575 -0.221479
+1.000043 0.911263 -0.015780
+1.000043 0.777616 -0.015780
+1.155723 0.777616 -0.015780
+1.155723 0.911263 -0.015780
+1.155723 0.911263 0.127052
+1.155723 0.777616 0.127052
+1.000043 0.777616 0.127052
+1.000043 0.911263 0.127052
+1.234397 0.911133 -0.086336
+1.234192 0.777488 -0.087021
+1.235114 0.777746 -0.229828
+1.235319 0.911392 -0.229143
+1.388133 0.776762 -0.078673
+1.388338 0.910407 -0.077988
+1.389260 0.910665 -0.220795
+1.389056 0.777020 -0.221479
+4 0 1 2 3
+4 4 3 2 5
+4 5 2 1 6
+4 6 7 4 5
+4 7 0 3 4
+4 6 1 0 7
+4 8 9 10 11
+4 12 13 14 15
+4 13 8 11 14
+4 12 15 16 17
+4 10 9 18 19
+4 20 21 22 23
+4 17 16 20 23
+4 19 18 22 21
+4 24 25 26 27
+4 28 29 30 31
+4 29 24 27 30
+4 28 31 32 33
+4 26 25 34 35
+4 36 37 38 39
+4 33 32 36 39
+4 35 34 38 37
+4 40 41 42 43
+4 44 45 46 47
+4 45 40 43 46
+4 44 47 48 49
+4 42 41 50 51
+4 52 53 54 55
+4 49 48 52 55
+4 51 50 54 53
+4 56 57 58 59
+4 60 61 62 63
+4 61 56 59 62
+4 60 63 64 65
+4 58 57 66 67
+4 68 69 70 71
+4 65 64 68 71
+4 67 66 70 69
+4 72 73 74 75
+4 76 77 78 79
+4 79 78 73 72
+4 76 80 81 77
+4 74 82 83 75
+4 84 85 86 87
+4 80 85 84 81
+4 82 87 86 83
+4 88 89 90 91
+4 92 93 94 95
+4 95 94 89 88
+4 92 96 97 93
+4 90 98 99 91
+4 100 101 102 103
+4 96 101 100 97
+4 98 103 102 99
+4 104 105 106 107
+4 108 109 110 111
+4 111 110 105 104
+4 108 112 113 109
+4 106 114 115 107
+4 116 117 118 119
+4 112 117 116 113
+4 114 119 118 115
+4 120 121 122 123
+4 124 125 126 127
+4 127 126 121 120
+4 124 128 129 125
+4 122 130 131 123
+4 132 133 134 135
+4 128 133 132 129
+4 130 135 134 131

+ 39 - 0
swd_probe/model/convert.py

@@ -0,0 +1,39 @@
+#!/usr/bin/python
+
+import plyfile
+import argparse
+
+parser = argparse.ArgumentParser(description='Convert a PLY file to C arrays.')
+parser.add_argument('input_file', help='the input PLY file')
+parser.add_argument('output_file', help='the output C file')
+args = parser.parse_args()
+
+# Open the PLY file
+plydata = plyfile.PlyData.read(args.input_file)
+
+# Extract the vertices
+vertices = plydata['vertex'].data
+num_vertices = len(vertices)
+
+with open(args.output_file, 'w') as f:
+    f.write('#define NUM_VERTICES %d\n' % num_vertices)
+    f.write('float vertexCoords[NUM_VERTICES][3] = {\n')
+    for i in range(num_vertices):
+        x, y, z = vertices[i][0], vertices[i][1], vertices[i][2]
+        f.write('  {%f, %f, %f},\n' % (x, y, z))
+    f.write('};')
+
+    # Extract the faces
+    faces = plydata['face'].data
+    num_faces = len(faces)
+    f.write('int edgeIndices[][3] = {\n')
+    for i in range(num_faces):
+        face = faces[i][0]
+        if len(face) == 3:
+            f.write('  {%d, %d, %d},\n' % (face[0], face[1], face[2]))
+        elif len(face) == 4:
+            # Convert 4-index face to 2-index edges
+            edges = [(face[0], face[1]), (face[1], face[2]), (face[2], face[3]), (face[3], face[0])]
+            for edge in edges:
+                f.write('  {%d, %d},\n' % (edge[0], edge[1]))
+    f.write('};\n')

+ 108 - 0
swd_probe/model/model_chip.h

@@ -0,0 +1,108 @@
+#define NUM_VERTICES 136
+float vertexCoords[NUM_VERTICES][3] = {
+    {1.000000, 1.000000, 0.152153},    {-1.000000, 1.000000, 0.152153},
+    {-1.000000, -1.000000, 0.152153},  {1.000000, -1.000000, 0.152153},
+    {1.000000, -1.000000, -0.185787},  {-1.000000, -1.000000, -0.185787},
+    {-1.000000, 1.000000, -0.185787},  {1.000000, 1.000000, -0.185787},
+    {-1.000043, -0.785071, -0.015780}, {-1.155724, -0.785071, -0.015780},
+    {-1.155724, -0.918718, -0.015780}, {-1.000043, -0.918718, -0.015780},
+    {-1.155724, -0.785071, 0.127052},  {-1.000043, -0.785071, 0.127052},
+    {-1.000043, -0.918718, 0.127052},  {-1.155724, -0.918718, 0.127052},
+    {-1.234192, -0.918846, -0.087021}, {-1.234397, -0.785201, -0.086336},
+    {-1.235319, -0.784943, -0.229143}, {-1.235114, -0.918588, -0.229828},
+    {-1.388133, -0.919573, -0.078673}, {-1.389056, -0.919314, -0.221479},
+    {-1.389261, -0.785669, -0.220795}, {-1.388338, -0.785927, -0.077988},
+    {-1.000043, -0.219627, -0.015780}, {-1.155724, -0.219627, -0.015780},
+    {-1.155724, -0.353273, -0.015780}, {-1.000043, -0.353273, -0.015780},
+    {-1.155724, -0.219627, 0.127052},  {-1.000043, -0.219627, 0.127052},
+    {-1.000043, -0.353273, 0.127052},  {-1.155724, -0.353273, 0.127052},
+    {-1.234192, -0.353402, -0.087021}, {-1.234397, -0.219756, -0.086336},
+    {-1.235319, -0.219498, -0.229143}, {-1.235114, -0.353143, -0.229828},
+    {-1.388133, -0.354128, -0.078673}, {-1.389056, -0.353870, -0.221479},
+    {-1.389261, -0.220224, -0.220795}, {-1.388338, -0.220482, -0.077988},
+    {-1.000043, 0.345818, -0.015780},  {-1.155724, 0.345818, -0.015780},
+    {-1.155724, 0.212172, -0.015780},  {-1.000043, 0.212172, -0.015780},
+    {-1.155724, 0.345818, 0.127052},   {-1.000043, 0.345818, 0.127052},
+    {-1.000043, 0.212172, 0.127052},   {-1.155724, 0.212172, 0.127052},
+    {-1.234192, 0.212043, -0.087021},  {-1.234397, 0.345689, -0.086336},
+    {-1.235319, 0.345947, -0.229143},  {-1.235114, 0.212301, -0.229828},
+    {-1.388133, 0.211317, -0.078673},  {-1.389056, 0.211575, -0.221479},
+    {-1.389261, 0.345221, -0.220795},  {-1.388338, 0.344962, -0.077988},
+    {-1.000043, 0.911263, -0.015780},  {-1.155724, 0.911263, -0.015780},
+    {-1.155724, 0.777617, -0.015780},  {-1.000043, 0.777617, -0.015780},
+    {-1.155724, 0.911263, 0.127052},   {-1.000043, 0.911263, 0.127052},
+    {-1.000043, 0.777617, 0.127052},   {-1.155724, 0.777617, 0.127052},
+    {-1.234192, 0.777488, -0.087021},  {-1.234397, 0.911133, -0.086336},
+    {-1.235319, 0.911392, -0.229143},  {-1.235114, 0.777746, -0.229828},
+    {-1.388133, 0.776762, -0.078673},  {-1.389056, 0.777020, -0.221479},
+    {-1.389261, 0.910665, -0.220795},  {-1.388338, 0.910407, -0.077988},
+    {1.000043, -0.785071, -0.015780},  {1.000043, -0.918718, -0.015780},
+    {1.155723, -0.918718, -0.015780},  {1.155723, -0.785071, -0.015780},
+    {1.155723, -0.785071, 0.127052},   {1.155723, -0.918718, 0.127052},
+    {1.000043, -0.918718, 0.127052},   {1.000043, -0.785071, 0.127052},
+    {1.234397, -0.785201, -0.086336},  {1.234192, -0.918846, -0.087021},
+    {1.235114, -0.918588, -0.229828},  {1.235319, -0.784943, -0.229143},
+    {1.388133, -0.919573, -0.078673},  {1.388338, -0.785927, -0.077988},
+    {1.389260, -0.785669, -0.220795},  {1.389056, -0.919314, -0.221479},
+    {1.000043, -0.219627, -0.015780},  {1.000043, -0.353273, -0.015780},
+    {1.155723, -0.353273, -0.015780},  {1.155723, -0.219627, -0.015780},
+    {1.155723, -0.219627, 0.127052},   {1.155723, -0.353273, 0.127052},
+    {1.000043, -0.353273, 0.127052},   {1.000043, -0.219627, 0.127052},
+    {1.234397, -0.219756, -0.086336},  {1.234192, -0.353402, -0.087021},
+    {1.235114, -0.353143, -0.229828},  {1.235319, -0.219498, -0.229143},
+    {1.388133, -0.354128, -0.078673},  {1.388338, -0.220482, -0.077988},
+    {1.389260, -0.220224, -0.220795},  {1.389056, -0.353870, -0.221479},
+    {1.000043, 0.345818, -0.015780},   {1.000043, 0.212172, -0.015780},
+    {1.155723, 0.212172, -0.015780},   {1.155723, 0.345818, -0.015780},
+    {1.155723, 0.345818, 0.127052},    {1.155723, 0.212172, 0.127052},
+    {1.000043, 0.212172, 0.127052},    {1.000043, 0.345818, 0.127052},
+    {1.234397, 0.345689, -0.086336},   {1.234192, 0.212043, -0.087021},
+    {1.235114, 0.212301, -0.229828},   {1.235319, 0.345947, -0.229143},
+    {1.388133, 0.211317, -0.078673},   {1.388338, 0.344962, -0.077988},
+    {1.389260, 0.345221, -0.220795},   {1.389056, 0.211575, -0.221479},
+    {1.000043, 0.911263, -0.015780},   {1.000043, 0.777616, -0.015780},
+    {1.155723, 0.777616, -0.015780},   {1.155723, 0.911263, -0.015780},
+    {1.155723, 0.911263, 0.127052},    {1.155723, 0.777616, 0.127052},
+    {1.000043, 0.777616, 0.127052},    {1.000043, 0.911263, 0.127052},
+    {1.234397, 0.911133, -0.086336},   {1.234192, 0.777488, -0.087021},
+    {1.235114, 0.777746, -0.229828},   {1.235319, 0.911392, -0.229143},
+    {1.388133, 0.776762, -0.078673},   {1.388338, 0.910407, -0.077988},
+    {1.389260, 0.910665, -0.220795},   {1.389056, 0.777020, -0.221479},
+};
+int edgeIndices[][3] = {
+    {0, 1},     {1, 2},     {2, 3},     {3, 0},     {4, 3},     {3, 2},     {2, 5},     {5, 4},
+    {5, 2},     {2, 1},     {1, 6},     {6, 5},     {6, 7},     {7, 4},     {4, 5},     {5, 6},
+    {7, 0},     {0, 3},     {3, 4},     {4, 7},     {6, 1},     {1, 0},     {0, 7},     {7, 6},
+    {8, 9},     {9, 10},    {10, 11},   {11, 8},    {12, 13},   {13, 14},   {14, 15},   {15, 12},
+    {13, 8},    {8, 11},    {11, 14},   {14, 13},   {12, 15},   {15, 16},   {16, 17},   {17, 12},
+    {10, 9},    {9, 18},    {18, 19},   {19, 10},   {20, 21},   {21, 22},   {22, 23},   {23, 20},
+    {17, 16},   {16, 20},   {20, 23},   {23, 17},   {19, 18},   {18, 22},   {22, 21},   {21, 19},
+    {24, 25},   {25, 26},   {26, 27},   {27, 24},   {28, 29},   {29, 30},   {30, 31},   {31, 28},
+    {29, 24},   {24, 27},   {27, 30},   {30, 29},   {28, 31},   {31, 32},   {32, 33},   {33, 28},
+    {26, 25},   {25, 34},   {34, 35},   {35, 26},   {36, 37},   {37, 38},   {38, 39},   {39, 36},
+    {33, 32},   {32, 36},   {36, 39},   {39, 33},   {35, 34},   {34, 38},   {38, 37},   {37, 35},
+    {40, 41},   {41, 42},   {42, 43},   {43, 40},   {44, 45},   {45, 46},   {46, 47},   {47, 44},
+    {45, 40},   {40, 43},   {43, 46},   {46, 45},   {44, 47},   {47, 48},   {48, 49},   {49, 44},
+    {42, 41},   {41, 50},   {50, 51},   {51, 42},   {52, 53},   {53, 54},   {54, 55},   {55, 52},
+    {49, 48},   {48, 52},   {52, 55},   {55, 49},   {51, 50},   {50, 54},   {54, 53},   {53, 51},
+    {56, 57},   {57, 58},   {58, 59},   {59, 56},   {60, 61},   {61, 62},   {62, 63},   {63, 60},
+    {61, 56},   {56, 59},   {59, 62},   {62, 61},   {60, 63},   {63, 64},   {64, 65},   {65, 60},
+    {58, 57},   {57, 66},   {66, 67},   {67, 58},   {68, 69},   {69, 70},   {70, 71},   {71, 68},
+    {65, 64},   {64, 68},   {68, 71},   {71, 65},   {67, 66},   {66, 70},   {70, 69},   {69, 67},
+    {72, 73},   {73, 74},   {74, 75},   {75, 72},   {76, 77},   {77, 78},   {78, 79},   {79, 76},
+    {79, 78},   {78, 73},   {73, 72},   {72, 79},   {76, 80},   {80, 81},   {81, 77},   {77, 76},
+    {74, 82},   {82, 83},   {83, 75},   {75, 74},   {84, 85},   {85, 86},   {86, 87},   {87, 84},
+    {80, 85},   {85, 84},   {84, 81},   {81, 80},   {82, 87},   {87, 86},   {86, 83},   {83, 82},
+    {88, 89},   {89, 90},   {90, 91},   {91, 88},   {92, 93},   {93, 94},   {94, 95},   {95, 92},
+    {95, 94},   {94, 89},   {89, 88},   {88, 95},   {92, 96},   {96, 97},   {97, 93},   {93, 92},
+    {90, 98},   {98, 99},   {99, 91},   {91, 90},   {100, 101}, {101, 102}, {102, 103}, {103, 100},
+    {96, 101},  {101, 100}, {100, 97},  {97, 96},   {98, 103},  {103, 102}, {102, 99},  {99, 98},
+    {104, 105}, {105, 106}, {106, 107}, {107, 104}, {108, 109}, {109, 110}, {110, 111}, {111, 108},
+    {111, 110}, {110, 105}, {105, 104}, {104, 111}, {108, 112}, {112, 113}, {113, 109}, {109, 108},
+    {106, 114}, {114, 115}, {115, 107}, {107, 106}, {116, 117}, {117, 118}, {118, 119}, {119, 116},
+    {112, 117}, {117, 116}, {116, 113}, {113, 112}, {114, 119}, {119, 118}, {118, 115}, {115, 114},
+    {120, 121}, {121, 122}, {122, 123}, {123, 120}, {124, 125}, {125, 126}, {126, 127}, {127, 124},
+    {127, 126}, {126, 121}, {121, 120}, {120, 127}, {124, 128}, {128, 129}, {129, 125}, {125, 124},
+    {122, 130}, {130, 131}, {131, 123}, {123, 122}, {132, 133}, {133, 134}, {134, 135}, {135, 132},
+    {128, 133}, {133, 132}, {132, 129}, {129, 128}, {130, 135}, {135, 134}, {134, 131}, {131, 130},
+};

+ 3187 - 0
swd_probe/swd_probe_app.c

@@ -0,0 +1,3187 @@
+
+
+#include "swd_probe_app.h"
+#include "swd_probe_icons.h"
+#include "jep106.h"
+#include "adi.h"
+
+static void render_callback(Canvas* const canvas, void* cb_ctx);
+static bool swd_message_process(AppFSM* ctx);
+static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data);
+static bool swd_execute_script(AppFSM* const ctx, const char* filename);
+
+static const GpioPin* gpios[] = {
+    &gpio_ext_pc0,
+    &gpio_ext_pc1,
+    &gpio_ext_pc3,
+    &gpio_ext_pb2,
+    &gpio_ext_pb3,
+    &gpio_ext_pa4,
+    &gpio_ext_pa6,
+    &gpio_ext_pa7};
+
+static const char* gpio_names[] = {"PC0", "PC1", "PC3", "PB2", "PB3", "PA4", "PA6", "PA7"};
+
+/* bit set: clock, else data */
+static const uint8_t gpio_direction_mask[6] =
+    {0b10101010, 0b01010101, 0b11001100, 0b00110011, 0b11110000, 0b00001111};
+
+const NotificationSequence seq_c_minor = {
+    &message_note_c4,
+    &message_delay_100,
+    &message_sound_off,
+    &message_delay_10,
+
+    &message_note_ds4,
+    &message_delay_100,
+    &message_sound_off,
+    &message_delay_10,
+
+    &message_note_g4,
+    &message_delay_100,
+    &message_sound_off,
+    &message_delay_10,
+
+    &message_vibro_on,
+    &message_delay_50,
+    &message_vibro_off,
+    NULL,
+};
+
+const NotificationSequence seq_error = {
+
+    &message_vibro_on,
+    &message_delay_50,
+    &message_vibro_off,
+
+    &message_note_g4,
+    &message_delay_100,
+    &message_sound_off,
+    &message_delay_10,
+
+    &message_note_c4,
+    &message_delay_500,
+    &message_sound_off,
+    &message_delay_10,
+    NULL,
+};
+
+const NotificationSequence* seq_sounds[] = {&seq_c_minor, &seq_error};
+
+static bool has_multiple_bits(uint8_t x) {
+    return (x & (x - 1)) != 0;
+}
+
+static uint8_t get_bit_num(uint8_t x) {
+    return (uint8_t)__builtin_ctz(x);
+}
+
+static const char* gpio_name(uint8_t mask) {
+    if(has_multiple_bits(mask)) {
+        return "Pxx";
+    }
+    uint8_t io = get_bit_num(mask);
+    if(io >= COUNT(gpio_names)) {
+        return "Pxx";
+    }
+
+    return gpio_names[io];
+}
+
+static void swd_configure_pins(AppFSM* const ctx, bool output) {
+    if(ctx->mode_page > ModePageFound && ctx->io_num_swc < 8 && ctx->io_num_swd < 8) {
+        furi_hal_gpio_init(
+            gpios[ctx->io_num_swc], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+        if(!output) {
+            furi_hal_gpio_init(
+                gpios[ctx->io_num_swd], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
+        } else {
+            furi_hal_gpio_init(
+                gpios[ctx->io_num_swd], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
+        }
+        return;
+    }
+
+    for(int io = 0; io < 8; io++) {
+        uint8_t bitmask = 1 << io;
+
+        /* if neither candidate for SWC nor SWD then skip */
+        if(!(ctx->io_swc & bitmask) && !(ctx->io_swd & bitmask)) {
+            furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
+            continue;
+        }
+
+        if(ctx->current_mask & bitmask) {
+            /* set for clock */
+            furi_hal_gpio_init(gpios[io], GpioModeOutputPushPull, GpioPullNo, GpioSpeedVeryHigh);
+        } else {
+            /* set for data */
+            if(!output) {
+                furi_hal_gpio_init(gpios[io], GpioModeInput, GpioPullUp, GpioSpeedVeryHigh);
+            } else {
+                furi_hal_gpio_init(
+                    gpios[io], GpioModeOutputOpenDrain, GpioPullUp, GpioSpeedVeryHigh);
+            }
+        }
+    }
+}
+
+static void swd_set_clock(AppFSM* const ctx, const uint8_t level) {
+    if(ctx->mode_page > ModePageFound && ctx->io_num_swc < 8) {
+        furi_hal_gpio_write(gpios[ctx->io_num_swc], level);
+        return;
+    }
+
+    for(int io = 0; io < 8; io++) {
+        uint8_t bitmask = 1 << io;
+
+        /* if no candidate for SWC then skip */
+        if(!(ctx->io_swc & bitmask)) {
+            continue;
+        }
+
+        if(ctx->current_mask & bitmask) {
+            furi_hal_gpio_write(gpios[io], level);
+        }
+    }
+}
+
+static void swd_set_data(AppFSM* const ctx, const uint8_t level) {
+    if(ctx->mode_page > ModePageFound && ctx->io_num_swd < 8) {
+        furi_hal_gpio_write(gpios[ctx->io_num_swd], level);
+        return;
+    }
+
+    for(int io = 0; io < 8; io++) {
+        uint8_t bitmask = 1 << io;
+
+        /* if no candidate for SWD then skip */
+        if(!(ctx->io_swd & bitmask)) {
+            continue;
+        }
+
+        if(!(ctx->current_mask & bitmask)) {
+            furi_hal_gpio_write(gpios[io], level);
+        }
+    }
+}
+
+static uint8_t swd_get_data(AppFSM* const ctx) {
+    if(ctx->mode_page > ModePageFound && ctx->io_num_swd < 8) {
+        return furi_hal_gpio_read(gpios[ctx->io_num_swd]);
+    }
+
+    uint8_t bits = 0;
+    for(int io = 0; io < 8; io++) {
+        uint8_t bitmask = 1 << io;
+
+        /* if no candidate for SWD then skip */
+        if(!(ctx->io_swd & bitmask)) {
+            continue;
+        }
+        bits |= furi_hal_gpio_read(gpios[io]) ? bitmask : 0;
+    }
+    return bits;
+}
+
+static void swd_clock_delay(AppFSM* const ctx) {
+    if(ctx->swd_clock_delay) {
+        furi_delay_us(ctx->swd_clock_delay);
+    }
+}
+
+static void swd_write_bit(AppFSM* const ctx, bool level) {
+    swd_set_clock(ctx, 0);
+    swd_set_data(ctx, level);
+    swd_clock_delay(ctx);
+    swd_set_clock(ctx, 1);
+    swd_clock_delay(ctx);
+    swd_set_clock(ctx, 0);
+}
+
+static uint8_t swd_read_bit(AppFSM* const ctx) {
+    swd_set_clock(ctx, 1);
+    swd_clock_delay(ctx);
+    swd_set_clock(ctx, 0);
+    uint8_t bits = swd_get_data(ctx);
+    swd_clock_delay(ctx);
+    swd_set_clock(ctx, 1);
+
+    return bits;
+}
+
+/* send a byte or less LSB-first */
+static void swd_write_byte(AppFSM* const ctx, const uint8_t data, size_t bits) {
+    for(size_t pos = 0; pos < bits; pos++) {
+        swd_write_bit(ctx, data & (1 << pos));
+    }
+}
+
+/* send a sequence of bytes LSB-first */
+static void swd_write(AppFSM* const ctx, const uint8_t* data, size_t bits) {
+    size_t byte_pos = 0;
+    while(bits > 0) {
+        size_t remain = (bits > 8) ? 8 : bits;
+        swd_write_byte(ctx, data[byte_pos++], remain);
+        bits -= remain;
+    }
+}
+
+static uint8_t swd_transfer(AppFSM* const ctx, bool ap, bool write, uint8_t a23, uint32_t* data) {
+    //notification_message(ctx->notification, &sequence_set_blue_255);
+    //notification_message(ctx->notification, &sequence_reset_red);
+
+    swd_set_data(ctx, false);
+    swd_configure_pins(ctx, true);
+
+    uint32_t idle = 0;
+    swd_write(ctx, (uint8_t*)&idle, ctx->swd_idle_bits);
+
+    uint8_t request[] = {0};
+
+    request[0] |= 0x01; /* start bit*/
+    request[0] |= ap ? 0x02 : 0; /* APnDP */
+    request[0] |= write ? 0 : 0x04; /* operation */
+    request[0] |= (a23 & 0x01) ? 0x08 : 0; /* A[2:3] */
+    request[0] |= (a23 & 0x02) ? 0x10 : 0; /* A[2:3] */
+    request[0] |= 0x80; /* park bit */
+    request[0] |= __builtin_parity(request[0]) ? 0x20 : 0; /* parity */
+
+    swd_write(ctx, request, sizeof(request) * 8);
+
+    /* turnaround cycle */
+    swd_configure_pins(ctx, false);
+
+    uint8_t ack = 0;
+
+    /* receive 3 ACK bits */
+    for(int pos = 0; pos < 3; pos++) {
+        ack >>= 1;
+        ack |= swd_read_bit(ctx) ? 0x04 : 0;
+    }
+
+    /* force ABORT/CTRL to always work */
+    if(!ap && a23 == 0) {
+        ack = 1;
+    }
+
+    if(ack != 0x01) {
+        //notification_message(ctx->notification, &sequence_reset_blue);
+        //notification_message(ctx->notification, &sequence_set_red_255);
+        return ack;
+    }
+
+    if(write) {
+        swd_write_bit(ctx, 0);
+        swd_configure_pins(ctx, true);
+
+        /* send 32 WDATA bits */
+        for(int pos = 0; pos < 32; pos++) {
+            swd_write_bit(ctx, *data & (1 << pos));
+        }
+
+        /* send parity bit */
+        swd_write_bit(ctx, __builtin_parity(*data));
+    } else {
+        *data = 0;
+        /* receive 32 RDATA bits */
+        for(int pos = 0; pos < 32; pos++) {
+            *data >>= 1;
+            *data |= swd_read_bit(ctx) ? 0x80000000 : 0;
+        }
+
+        /* receive parity bit */
+        bool parity = swd_read_bit(ctx);
+
+        if(parity != __builtin_parity(*data)) {
+            //notification_message(ctx->notification, &sequence_reset_blue);
+            //notification_message(ctx->notification, &sequence_set_red_255);
+            return 8;
+        }
+    }
+    swd_set_data(ctx, false);
+    swd_configure_pins(ctx, true);
+    //notification_message(ctx->notification, &sequence_reset_blue);
+
+    return ack;
+}
+
+/* A line reset is achieved by holding the data signal HIGH for at least 50 clock cycles, followed by at least two idle cycles. */
+static void swd_line_reset(AppFSM* const ctx) {
+    //notification_message(ctx->notification, &sequence_set_red_255);
+    for(int bitcount = 0; bitcount < 50; bitcount += 8) {
+        swd_write_byte(ctx, 0xFF, 8);
+    }
+    swd_write_byte(ctx, 0, 8);
+    ctx->dp_regs.select_ok = false;
+    //notification_message(ctx->notification, &sequence_reset_red);
+}
+
+static void swd_abort(AppFSM* const ctx) {
+    uint32_t dpidr;
+
+    /* first reset the line */
+    swd_line_reset(ctx);
+    swd_transfer(ctx, false, false, 0, &dpidr);
+    uint32_t abort = 0x0E;
+    swd_transfer(ctx, false, true, 0, &abort);
+}
+
+static void swd_abort_simple(AppFSM* const ctx) {
+    uint32_t abort = 0x0E;
+    swd_transfer(ctx, false, true, 0, &abort);
+
+    uint32_t dpidr;
+    if(swd_transfer(ctx, false, false, 0, &dpidr) != 1) {
+        swd_abort(ctx);
+    }
+}
+
+static uint8_t swd_select(AppFSM* const ctx, uint8_t ap_sel, uint8_t ap_bank, uint8_t dp_bank) {
+    uint32_t bank_reg = (ap_sel << 24) | ((ap_bank & 0x0F) << 4) | (dp_bank & 0x0F);
+
+    if(ctx->dp_regs.select_ok && bank_reg == ctx->dp_regs.select) {
+        return 1;
+    }
+
+    uint8_t ret = swd_transfer(ctx, false, true, REG_SELECT, &bank_reg);
+    if(ret != 1) {
+        ctx->dp_regs.select_ok = false;
+        DBG("failed: %d", ret);
+        return ret;
+    }
+
+    ctx->dp_regs.select = bank_reg;
+    ctx->dp_regs.select_ok = true;
+    return ret;
+}
+
+static uint8_t
+    swd_read_dpbank(AppFSM* const ctx, uint8_t dp_off, uint8_t dp_bank, uint32_t* data) {
+    uint8_t ret = 0;
+
+    /* select target bank */
+    if(dp_bank < 0x10) {
+        uint8_t ret = swd_select(ctx, 0, 0, dp_bank);
+        if(ret != 1) {
+            DBGS("swd_select failed");
+            return ret;
+        }
+    }
+
+    /* read data from it */
+    *data = 0;
+    ret = swd_transfer(ctx, false, false, dp_off, data);
+    if(ret != 1) {
+        DBG("failed: %d", ret);
+        return ret;
+    }
+    return ret;
+}
+
+static uint8_t
+    swd_write_dpbank(AppFSM* const ctx, uint8_t dp_off, uint8_t dp_bank, uint32_t* data) {
+    uint8_t ret = 0;
+
+    /* select target bank */
+    if(dp_bank < 0x10) {
+        ret = swd_select(ctx, 0, 0, dp_bank);
+        if(ret != 1) {
+            DBGS("swd_select failed");
+            return ret;
+        }
+    }
+
+    /* write it */
+    ret = swd_transfer(ctx, false, true, dp_off, data);
+    if(ret != 1) {
+        DBG("failed: %d", ret);
+        return ret;
+    }
+    return ret;
+}
+
+static uint8_t swd_read_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) {
+    /* select target bank */
+    uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
+    if(ret != 1) {
+        DBGS("swd_select failed");
+        return ret;
+    }
+    ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
+    *data = 0;
+    ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
+    if(ret != 1) {
+        DBG("failed: %d", ret);
+        return ret;
+    }
+    return ret;
+}
+
+static uint8_t swd_read_ap_single(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t* data) {
+    uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
+    if(ret != 1) {
+        DBGS("swd_select failed");
+        return ret;
+    }
+    *data = 0;
+    ret = swd_transfer(ctx, true, false, (ap_off >> 2) & 3, data);
+    if(ret != 1) {
+        DBG("failed: %d", ret);
+        return ret;
+    }
+    return ret;
+}
+
+static uint8_t swd_write_ap(AppFSM* const ctx, uint8_t ap, uint8_t ap_off, uint32_t data) {
+    uint8_t ret = swd_select(ctx, ap, (ap_off >> 4) & 0x0F, 0);
+    if(ret != 1) {
+        DBGS("swd_select failed");
+        return ret;
+    }
+    ret = swd_transfer(ctx, true, true, (ap_off >> 2) & 3, &data);
+    if(ret != 1) {
+        DBG("failed: %d", ret);
+        return ret;
+    }
+    return ret;
+}
+
+static uint8_t swd_write_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t data) {
+    uint8_t ret = 0;
+    uint32_t csw = 0x23000002;
+
+    ret |= swd_write_ap(ctx, ap, MEMAP_CSW, csw);
+    ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
+    ret |= swd_write_ap(ctx, ap, MEMAP_DRW, data);
+    DBG("write 0x%08lX to 0x%08lX", data, address);
+
+    if(ret != 1) {
+        swd_abort(ctx);
+    }
+    return ret;
+}
+
+uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data) {
+    uint8_t ret = 0;
+    uint32_t csw = 0x23000002;
+
+    ret |= swd_write_ap(ctx, ap, MEMAP_CSW, csw);
+    ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
+    ret |= swd_read_ap(ctx, ap, MEMAP_DRW, data);
+
+    if(ret != 1) {
+        DBG("read from 0x%08lX failed", address);
+        swd_abort(ctx);
+    } else {
+        DBG("read 0x%08lX from 0x%08lX", *data, address);
+    }
+    return ret;
+}
+
+static uint8_t swd_read_memory_block(
+    AppFSM* const ctx,
+    uint8_t ap,
+    uint32_t address,
+    uint8_t* buf,
+    uint32_t len) {
+    uint8_t ret = 0;
+    uint32_t data = 0;
+    uint32_t csw = 0x23000012;
+
+    ret |= swd_write_ap(ctx, ap, MEMAP_CSW, csw);
+    ret |= swd_write_ap(ctx, ap, MEMAP_TAR, address);
+    ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, &data);
+
+    for(size_t pos = 0; pos < len; pos += 4) {
+        data = 0xDEADBEEF;
+        ret |= swd_read_ap_single(ctx, ap, MEMAP_DRW, &data);
+        DBG("read %lX", data);
+
+        memcpy(&buf[pos], &data, 4);
+
+        if(ret != 1) {
+            swd_abort(ctx);
+            return ret;
+        }
+    }
+    return ret;
+}
+
+static uint32_t swd_detect(AppFSM* const ctx) {
+    swd_set_data(ctx, false);
+    swd_configure_pins(ctx, true);
+
+    uint8_t data[] = {0xA5};
+    swd_write(ctx, data, sizeof(data) * 8);
+
+    /* turnaround cycle */
+    swd_configure_pins(ctx, false);
+
+    uint8_t ack_bits[3];
+    uint8_t rdata[32];
+
+    /* receive 3 ACK bits */
+    for(int pos = 0; pos < 3; pos++) {
+        ack_bits[pos] = swd_read_bit(ctx);
+    }
+
+    /* receive 32 RDATA bits */
+    for(int pos = 0; pos < 32; pos++) {
+        rdata[pos] = swd_read_bit(ctx);
+    }
+
+    /* receive parity bit */
+    uint8_t parity = swd_read_bit(ctx);
+
+    for(int io = 0; io < 8; io++) {
+        uint8_t bitmask = 1 << io;
+
+        /* skip if it's a clock */
+        if(ctx->current_mask & bitmask) {
+            continue;
+        }
+
+        uint8_t ack = 0;
+        for(int pos = 0; pos < 3; pos++) {
+            ack >>= 1;
+            ack |= (ack_bits[pos] & bitmask) ? 4 : 0;
+        }
+
+        uint32_t dpidr = 0;
+        for(int pos = 0; pos < 32; pos++) {
+            dpidr >>= 1;
+            dpidr |= (rdata[pos] & bitmask) ? 0x80000000 : 0;
+        }
+
+        if(ack == 1 && dpidr != 0 && dpidr != 0xFFFFFFFF) {
+            bool received_parity = (parity & bitmask);
+            if(__builtin_parity(dpidr) == received_parity) {
+                ctx->dp_regs.dpidr = dpidr;
+                ctx->dp_regs.dpidr_ok = true;
+                ctx->detected = true;
+                ctx->io_swd = bitmask;
+                ctx->io_swc &= ctx->current_mask;
+                LOG("swd_detect: data: %08lX, io_swd %02X, io_swc %02X",
+                    dpidr,
+                    ctx->io_swd,
+                    ctx->io_swc);
+
+                if(!has_multiple_bits(ctx->io_swc)) {
+                    ctx->io_num_swd = get_bit_num(ctx->io_swd);
+                    ctx->io_num_swc = get_bit_num(ctx->io_swc);
+                }
+            }
+        }
+    }
+    swd_set_data(ctx, false);
+    swd_configure_pins(ctx, true);
+
+    return 0;
+}
+
+static void swd_scan(AppFSM* const ctx) {
+    /* To switch SWJ-DP from JTAG to SWD operation:
+        1. Send at least 50 SWCLKTCK cycles with SWDIOTMS HIGH. This ensures that the current interface is in its reset state. The JTAG interface only detects the 16-bit JTAG-to-SWD sequence starting from the Test-Logic-Reset state.
+        2. Send the 16-bit JTAG-to-SWD select sequence 0x79e7 on SWDIOTMS.
+        3. Send at least 50 SWCLKTCK cycles with SWDIOTMS HIGH. This ensures that if SWJ-DP was already in SWD operation before sending the select sequence, the SWD interface enters line reset state.
+    */
+    swd_configure_pins(ctx, true);
+
+    /* reset JTAG interface */
+    for(int bitcount = 0; bitcount < 50; bitcount += 8) {
+        swd_write_byte(ctx, 0xFF, 8);
+    }
+
+    /* Send the 16-bit JTAG-to-SWD select sequence */
+    swd_write_byte(ctx, 0x9E, 8);
+    swd_write_byte(ctx, 0xE7, 8);
+
+    /* resynchronize SWD */
+    swd_line_reset(ctx);
+
+    swd_detect(ctx);
+}
+
+static bool swd_ensure_powerup(AppFSM* const ctx) {
+    bool ret = true;
+
+    if(!(ctx->dp_regs.ctrlstat & (CSYSPWRUPREQ | CDBGPWRUPREQ))) {
+        DBGS("no (CSYSPWRUPREQ | CDBGPWRUPREQ)");
+
+        /* fetch current CTRL/STAT */
+        DBGS(" - Fetch CTRL/STAT");
+        ctx->dp_regs.ctrlstat_ok =
+            swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat) == 1;
+        DBG("     %08lX %s", ctx->dp_regs.ctrlstat, ctx->dp_regs.ctrlstat_ok ? "OK" : "FAIL");
+        /* enable requests */
+        ctx->dp_regs.ctrlstat |= (CSYSPWRUPREQ | CDBGPWRUPREQ);
+
+        swd_write_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat);
+
+        ret = false;
+    }
+    if(!(ctx->dp_regs.ctrlstat & CDBGPWRUPACK)) {
+        DBGS("no CDBGPWRUPACK");
+        /* fetch current CTRL/STAT */
+        swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat);
+        ret = false;
+    }
+
+    if(!ret) {
+        DBGS(" - Fetch CTRL/STAT");
+        ctx->dp_regs.ctrlstat_ok =
+            swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat) == 1;
+        DBG("     %08lX %s", ctx->dp_regs.ctrlstat, ctx->dp_regs.ctrlstat_ok ? "OK" : "FAIL");
+    }
+
+    return ret;
+}
+
+static void swd_apscan_reset(AppFSM* const ctx) {
+    for(size_t reset_ap = 0; reset_ap < COUNT(ctx->apidr_info); reset_ap++) {
+        ctx->apidr_info[reset_ap].tested = false;
+    }
+}
+
+static bool swd_apscan_test(AppFSM* const ctx, uint32_t ap) {
+    furi_assert(ctx);
+    furi_assert(ap < sizeof(ctx->apidr_info));
+    bool ret = true;
+
+    ctx->apidr_info[ap].tested = true;
+
+    uint32_t data = 0;
+    if(swd_read_ap(ctx, ap, AP_IDR, &data) != 1) {
+        swd_abort(ctx);
+        return false;
+    }
+    if(data == 0) {
+        return false;
+    }
+    DBG("AP%lu detected", ap);
+    ctx->apidr_info[ap].ok = true;
+    ctx->apidr_info[ap].revision = (data >> 24) & 0x0F;
+    ctx->apidr_info[ap].designer = (data >> 17) & 0x3FF;
+    ctx->apidr_info[ap].class = (data >> 13) & 0x0F;
+    ctx->apidr_info[ap].variant = (data >> 4) & 0x0F;
+    ctx->apidr_info[ap].type = (data >> 0) & 0x0F;
+
+    if(swd_read_ap(ctx, ap, AP_BASE, &ctx->apidr_info[ap].base) != 1) {
+        swd_abort(ctx);
+        ret = false;
+    }
+    return ret;
+}
+
+/**************************  script helpers  **************************/
+
+static void swd_script_log(ScriptContext* ctx, FuriLogLevel level, const char* format, ...) {
+    bool commandline = false;
+    ScriptContext* cur = ctx;
+    FuriString* buffer = furi_string_alloc();
+    va_list argp;
+    va_start(argp, format);
+
+    do {
+        if(cur == ctx->app->commandline) {
+            commandline = true;
+        }
+        cur = cur->parent;
+    } while(cur);
+
+    if(commandline) {
+        const char* prefix = "";
+
+        switch(level) {
+        case FuriLogLevelWarn:
+            prefix = "Warning: ";
+            break;
+        case FuriLogLevelError:
+            prefix = "ERROR: ";
+            break;
+        default:
+            break;
+        }
+
+        furi_string_cat_str(buffer, prefix);
+        furi_string_cat_printf(buffer, format, argp);
+        furi_string_cat_str(buffer, "\n");
+
+        if(!usb_uart_tx_data(
+               ctx->app->uart, (uint8_t*)furi_string_get_cstr(buffer), furi_string_size(buffer))) {
+            DBGS("Sending via USB failed");
+        }
+    } else {
+        LOG(furi_string_get_cstr(buffer));
+    }
+    va_end(argp);
+    furi_string_free(buffer);
+}
+
+/* read characters until newline was read */
+static bool swd_script_seek_newline(ScriptContext* ctx) {
+    while(true) {
+        uint8_t ch = 0;
+
+        if(ctx->script_file) {
+            if(storage_file_read(ctx->script_file, &ch, 1) != 1) {
+                return false;
+            }
+        } else {
+            ch = ctx->line_data[ctx->line_pos];
+            if(ch == 0) {
+                return false;
+            }
+            ctx->line_pos++;
+        }
+        if(ch == '\n') {
+            return true;
+        }
+    }
+}
+
+/* read whitespaces until the next character is read. 
+   returns false if EOF or newline was read */
+static bool swd_script_skip_whitespace(ScriptContext* ctx) {
+    while(true) {
+        uint8_t ch = 0;
+        uint64_t start_pos = 0;
+
+        if(ctx->script_file) {
+            start_pos = storage_file_tell(ctx->script_file);
+
+            if(storage_file_read(ctx->script_file, &ch, 1) != 1) {
+                return false;
+            }
+        } else {
+            start_pos = ctx->line_pos;
+            ch = ctx->line_data[ctx->line_pos];
+
+            if(ch == 0) {
+                return false;
+            }
+            ctx->line_pos++;
+        }
+        if(ch == '\n') {
+            return false;
+        }
+        if(ch != ' ') {
+            if(ctx->script_file) {
+                storage_file_seek(ctx->script_file, start_pos, true);
+            } else {
+                ctx->line_pos = start_pos;
+            }
+            return true;
+        }
+    }
+}
+
+static bool swd_script_get_string(ScriptContext* ctx, char* str, size_t max_length) {
+    bool quot = false;
+    size_t pos = 0;
+
+    str[pos] = '\000';
+
+    while(true) {
+        char ch = 0;
+        uint64_t start_pos = 0;
+
+        if(ctx->script_file) {
+            start_pos = storage_file_tell(ctx->script_file);
+
+            if(storage_file_read(ctx->script_file, &ch, 1) != 1) {
+                DBGS("end reached");
+                return false;
+            }
+        } else {
+            start_pos = ctx->line_pos;
+            ch = ctx->line_data[ctx->line_pos];
+
+            if(ch == 0) {
+                DBGS("end reached");
+                return false;
+            }
+            ctx->line_pos++;
+        }
+
+        if(ch == '"') {
+            quot = !quot;
+            continue;
+        }
+        if(!quot) {
+            if(ch == ' ') {
+                break;
+            }
+            if(ch == '\r' || ch == '\n') {
+                if(ctx->script_file) {
+                    storage_file_seek(ctx->script_file, start_pos, true);
+                } else {
+                    ctx->line_pos = start_pos;
+                }
+                break;
+            }
+        }
+        if(pos + 2 > max_length) {
+            DBGS("too long");
+            return false;
+        }
+        str[pos++] = ch;
+        str[pos] = '\000';
+    }
+    DBG("got '%s'", str);
+
+    return true;
+}
+
+static bool swd_script_get_number(ScriptContext* ctx, uint32_t* number) {
+    char str[16];
+
+    if(!swd_script_get_string(ctx, str, sizeof(str))) {
+        DBGS("could not get string");
+        return false;
+    }
+    DBG("got '%s'", str);
+
+    size_t pos = 0;
+    *number = 0;
+
+    /* hex number? */
+    if(!strncmp(str, "0x", 2)) {
+        pos += 2;
+        while(str[pos]) {
+            uint8_t ch = str[pos++];
+            uint8_t ch_num = ch - '0';
+            uint8_t ch_hex = (ch & ~0x20) - 'A';
+
+            *number <<= 4;
+
+            if(ch_num <= 10) {
+                *number += ch_num;
+            } else if(ch_hex <= 5) {
+                *number += 10 + ch_hex;
+            } else {
+                return false;
+            }
+        }
+    } else {
+        while(str[pos]) {
+            uint8_t ch = str[pos++];
+            uint8_t ch_num = ch - '0';
+
+            *number *= 10;
+
+            if(ch_num < 10) {
+                *number += ch_num;
+            } else {
+                return false;
+            }
+        }
+    }
+
+    return true;
+}
+
+static void swd_script_gui_refresh(ScriptContext* ctx) {
+    if(furi_message_queue_get_count(ctx->app->event_queue) > 0) {
+        swd_message_process(ctx->app);
+    }
+    if(!ctx->status_ignore) {
+        DBG("Status: %s", ctx->app->state_string);
+        view_port_update(ctx->app->view_port);
+    }
+}
+
+/************************** script functions **************************/
+
+static bool swd_scriptfunc_comment(ScriptContext* ctx) {
+    DBGS("comment");
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_label(ScriptContext* ctx) {
+    char label[256];
+    DBGS("label");
+
+    swd_script_skip_whitespace(ctx);
+    if(!swd_script_get_string(ctx, label, sizeof(label))) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse label");
+        return false;
+    }
+
+    if(!strcmp(label, ctx->goto_label)) {
+        ctx->goto_active = false;
+        DBG("matches '%s'", ctx->goto_label);
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_goto(ScriptContext* ctx) {
+    DBGS("goto");
+
+    swd_script_skip_whitespace(ctx);
+
+    if(!swd_script_get_string(ctx, ctx->goto_label, sizeof(ctx->goto_label))) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse target label");
+        return false;
+    }
+
+    /* start from beginning and rerun starting from label */
+    ctx->goto_active = true;
+    ctx->restart = true;
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+#include <toolbox/path.h>
+
+static bool swd_scriptfunc_call(ScriptContext* ctx) {
+    DBGS("call");
+
+    swd_script_skip_whitespace(ctx);
+
+    /* fetch previous file directory */
+    FuriString* filepath = furi_string_alloc();
+    path_extract_dirname(ctx->filename, filepath);
+    // strncpy(filename, ctx->filename, sizeof(filename));
+
+    char filename[MAX_FILE_LENGTH] = {};
+    bool success = false;
+    do {
+        /* append filename */
+        if(!swd_script_get_string(ctx, filename, sizeof(filename))) {
+            swd_script_log(ctx, FuriLogLevelError, "failed to parse filename");
+            break;
+        }
+
+        swd_script_seek_newline(ctx);
+        /* append extension */
+        furi_string_cat_str(filepath, ".swd");
+
+        bool ret = swd_execute_script(ctx->app, furi_string_get_cstr(filepath));
+
+        if(!ret) {
+            swd_script_log(
+                ctx, FuriLogLevelError, "failed to exec '%s'", furi_string_get_cstr(filepath));
+            break;
+        }
+
+        success = true;
+    } while(false);
+    furi_string_free(filepath);
+
+    return success;
+}
+
+static bool swd_scriptfunc_status(ScriptContext* ctx) {
+    uint32_t status = 1;
+    DBGS("status");
+
+    swd_script_skip_whitespace(ctx);
+    swd_script_get_number(ctx, &status);
+
+    ctx->status_ignore = (status == 0);
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_errors(ScriptContext* ctx) {
+    char type[32];
+    DBGS("errors");
+
+    swd_script_skip_whitespace(ctx);
+
+    if(!swd_script_get_string(ctx, type, sizeof(type))) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse");
+        return false;
+    }
+
+    if(!strcmp(type, "ignore")) {
+        ctx->errors_ignore = true;
+    }
+    if(!strcmp(type, "fail")) {
+        ctx->errors_ignore = false;
+    }
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_beep(ScriptContext* ctx) {
+    uint32_t sound = 0;
+    DBGS("beep");
+
+    swd_script_skip_whitespace(ctx);
+    swd_script_get_number(ctx, &sound);
+
+    notification_message_block(ctx->app->notification, seq_sounds[sound]);
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_message(ScriptContext* ctx) {
+    uint32_t wait_time = 0;
+    char message[256];
+    char type[256];
+    bool success = true;
+    bool show_dialog = false;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &wait_time)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse wait_time");
+        return false;
+    }
+
+    if(!swd_script_get_string(ctx, message, sizeof(message))) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse message");
+        return false;
+    }
+
+    if(swd_script_get_string(ctx, type, sizeof(type))) {
+        if(!strcmp(type, "dialog")) {
+            show_dialog = true;
+        }
+    }
+
+    if(wait_time <= 60 * 1000) {
+        strncpy(ctx->app->state_string, message, sizeof(ctx->app->state_string));
+        swd_script_gui_refresh(ctx);
+        furi_delay_ms(wait_time);
+        if(show_dialog) {
+            DialogMessage* message = dialog_message_alloc();
+            dialog_message_set_header(message, "SWD Probe", 16, 2, AlignLeft, AlignTop);
+            dialog_message_set_icon(message, &I_app, 3, 2);
+            dialog_message_set_text(message, ctx->app->state_string, 3, 16, AlignLeft, AlignTop);
+            dialog_message_set_buttons(message, "Abort", "Ok", NULL);
+            success = dialog_message_show(ctx->app->dialogs, message) == DialogMessageButtonCenter;
+            dialog_message_free(message);
+        }
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_swd_idle_bits(ScriptContext* ctx) {
+    uint32_t swd_idle_bits = 0;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &swd_idle_bits)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse");
+        return false;
+    }
+
+    if(swd_idle_bits <= 32) {
+        ctx->app->swd_idle_bits = swd_idle_bits;
+    } else {
+        swd_script_log(ctx, FuriLogLevelError, "value must be between 1 and 32");
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_swd_clock_delay(ScriptContext* ctx) {
+    uint32_t swd_clock_delay = 0;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &swd_clock_delay)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse");
+        return false;
+    }
+
+    if(swd_clock_delay <= 1000000) {
+        ctx->app->swd_clock_delay = swd_clock_delay;
+    } else {
+        swd_script_log(ctx, FuriLogLevelError, "value must be between 1 and 1000000");
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_maxtries(ScriptContext* ctx) {
+    uint32_t max_tries = 0;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &max_tries)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse");
+        return false;
+    }
+
+    if(max_tries >= 1 && max_tries <= 1024) {
+        ctx->max_tries = max_tries;
+    } else {
+        DBGS("value must be between 1 and 1024");
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_blocksize(ScriptContext* ctx) {
+    uint32_t block_size = 0;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &block_size)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse");
+        return false;
+    }
+
+    if(block_size >= 4 && block_size <= 0x1000) {
+        ctx->block_size = block_size;
+    } else {
+        swd_script_log(ctx, FuriLogLevelError, "value must be between 4 and 4096");
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_apselect(ScriptContext* ctx) {
+    uint32_t ap = 0;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &ap)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse AP");
+        return false;
+    }
+
+    if(!swd_apscan_test(ctx->app, ap)) {
+        swd_script_log(ctx, FuriLogLevelError, "no selected AP");
+        return false;
+    }
+
+    ctx->selected_ap = ap;
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_apscan(ScriptContext* ctx) {
+    DBGS("Scanning APs");
+    for(uint32_t ap = 0; ap < 255; ap++) {
+        snprintf(ctx->app->state_string, sizeof(ctx->app->state_string), "Scan AP %lu", ap);
+        swd_script_gui_refresh(ctx);
+        if(swd_apscan_test(ctx->app, ap)) {
+            DBG("  AP%lu detected", ap);
+        }
+    }
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_abort(ScriptContext* ctx) {
+    DBGS("Aborting");
+    swd_abort(ctx->app);
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_mem_dump(ScriptContext* ctx) {
+    char filename[MAX_FILE_LENGTH];
+    uint32_t address = 0;
+    uint32_t length = 0;
+    uint32_t flags = 0;
+    bool success = true;
+
+    /* get file */
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_string(ctx, filename, sizeof(filename))) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse filename");
+        return false;
+    }
+    /* get address */
+    if(!swd_script_get_number(ctx, &address)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse address");
+        return false;
+    }
+
+    /* get length */
+    if(!swd_script_get_number(ctx, &length)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse length");
+        return false;
+    }
+
+    /* get flags */
+    if(swd_script_get_number(ctx, &flags)) {
+        DBGS("found extra flags");
+    }
+
+    LOG("would dump %08lX, len %08lX into %s", address, length, filename);
+
+    File* dump = storage_file_alloc(ctx->app->storage);
+
+    if(!storage_file_open(dump, filename, FSAM_WRITE, FSOM_CREATE_ALWAYS)) {
+        storage_file_free(dump);
+        snprintf(ctx->app->state_string, sizeof(ctx->app->state_string), "Failed to create file");
+        swd_script_gui_refresh(ctx);
+        notification_message_block(ctx->app->notification, &seq_error);
+        return false;
+    }
+
+    if(ctx->block_size == 0) {
+        ctx->block_size = 0x100;
+    }
+    if(ctx->block_size > 0x1000) {
+        ctx->block_size = 0x1000;
+    }
+
+    uint8_t* buffer = malloc(ctx->block_size);
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+    for(uint32_t pos = 0; pos < length; pos += ctx->block_size) {
+        if((pos & 0xFF) == 0) {
+            int pct = pos * 100 / length;
+            snprintf(
+                ctx->app->state_string,
+                sizeof(ctx->app->state_string),
+                "Dump %08lX (%d%%)",
+                pos,
+                pct);
+            swd_script_gui_refresh(ctx);
+        }
+
+        bool read_ok = false;
+
+        for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
+            if(ctx->abort) {
+                DBGS("aborting read");
+                break;
+            }
+            uint32_t ret = 0;
+
+            if(ctx->block_size > 4) {
+                ret = swd_read_memory_block(
+                    ctx->app, ctx->selected_ap, address + pos, buffer, ctx->block_size);
+            } else {
+                ret =
+                    swd_read_memory(ctx->app, ctx->selected_ap, address + pos, (uint32_t*)buffer);
+            }
+            read_ok = (ret == 1);
+
+            if(!read_ok) {
+                snprintf(
+                    ctx->app->state_string,
+                    sizeof(ctx->app->state_string),
+                    "Failed at 0x%08lX",
+                    address + pos);
+                swd_script_gui_refresh(ctx);
+                furi_delay_ms(100);
+            } else {
+                break;
+            }
+        }
+        if(ctx->abort) {
+            DBGS("aborting");
+            break;
+        }
+
+        if(!read_ok) {
+            /* flags == 1: "continue reading even if it fails" */
+            /* flags == 2: "its okay if cannot dump fully" */
+            if(flags & 1) {
+                /* set all content to a known value as indication */
+                for(size_t fill_pos = 0; fill_pos < ctx->block_size; fill_pos += 4) {
+                    *((uint32_t*)&buffer[fill_pos]) = 0xDEADFACE;
+                }
+            } else if(flags & 2) {
+                success = (pos > 0);
+                break;
+            } else {
+                notification_message_block(ctx->app->notification, &seq_error);
+                success = false;
+                break;
+            }
+        }
+        storage_file_write(dump, buffer, ctx->block_size);
+    }
+
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    storage_file_close(dump);
+    swd_script_seek_newline(ctx);
+    free(buffer);
+
+    return success;
+}
+
+static bool swd_scriptfunc_mem_write(ScriptContext* ctx) {
+    uint32_t address = 0;
+    uint32_t data = 0;
+    bool success = true;
+
+    /* get file */
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get address */
+    if(!swd_script_get_number(ctx, &address)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse address");
+        return false;
+    }
+
+    /* get data */
+    if(!swd_script_get_number(ctx, &data)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse data");
+        return false;
+    }
+
+    DBG("write %08lX to %08lX", data, address);
+
+    bool access_ok = false;
+    for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
+        if(ctx->abort) {
+            DBGS("aborting");
+            break;
+        }
+
+        furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+        access_ok = swd_write_memory(ctx->app, ctx->selected_ap, address, data) == 1;
+        access_ok |= ctx->errors_ignore;
+        swd_read_memory(ctx->app, ctx->selected_ap, address, &data);
+        furi_mutex_release(ctx->app->swd_mutex);
+
+        DBG("read %08lX from %08lX", data, address);
+
+        if(!access_ok) {
+            snprintf(
+                ctx->app->state_string,
+                sizeof(ctx->app->state_string),
+                "Failed write 0x%08lX",
+                address);
+            swd_script_gui_refresh(ctx);
+        } else {
+            break;
+        }
+    }
+
+    if(!access_ok) {
+        notification_message_block(ctx->app->notification, &seq_error);
+        success = false;
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_mem_read(ScriptContext* ctx) {
+    uint32_t address = 0;
+    bool success = true;
+
+    /* get file */
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get address */
+    if(!swd_script_get_number(ctx, &address)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse address");
+        return false;
+    }
+
+    DBG("read from %08lX", address);
+
+    uint32_t data = 0;
+    bool access_ok = false;
+    for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
+        if(ctx->abort) {
+            DBGS("aborting");
+            break;
+        }
+
+        furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+        access_ok = swd_read_memory(ctx->app, ctx->selected_ap, address, &data) == 1;
+        furi_mutex_release(ctx->app->swd_mutex);
+
+        if(!access_ok) {
+            swd_script_log(ctx, FuriLogLevelError, "Failed to read from %08lX", address);
+            snprintf(
+                ctx->app->state_string,
+                sizeof(ctx->app->state_string),
+                "Failed read 0x%08lX",
+                address);
+            swd_script_gui_refresh(ctx);
+        } else {
+            swd_script_log(ctx, FuriLogLevelDefault, "%08lX", data);
+            break;
+        }
+    }
+
+    if(!access_ok) {
+        notification_message_block(ctx->app->notification, &seq_error);
+        success = false;
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_mem_ldmst(ScriptContext* ctx) {
+    uint32_t address = 0;
+    uint32_t data = 0;
+    uint32_t mask = 0;
+    bool success = true;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get address */
+    if(!swd_script_get_number(ctx, &address)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse address");
+        return false;
+    }
+
+    /* get data */
+    if(!swd_script_get_number(ctx, &data)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse data");
+        return false;
+    }
+
+    /* get mask */
+    if(!swd_script_get_number(ctx, &mask)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse mask");
+        return false;
+    }
+
+    LOG("write %08lX to %08lX, mask %08lX", data, address, mask);
+
+    bool access_ok = false;
+    uint32_t modified = 0;
+    for(uint32_t tries = 0; tries < ctx->max_tries; tries++) {
+        if(ctx->abort) {
+            DBGS("aborting");
+            break;
+        }
+        furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+        access_ok = swd_read_memory(ctx->app, ctx->selected_ap, address, &modified) == 1;
+        modified = (modified & mask) | data;
+        access_ok &= swd_write_memory(ctx->app, ctx->selected_ap, address, modified) == 1;
+
+        furi_mutex_release(ctx->app->swd_mutex);
+        access_ok |= ctx->errors_ignore;
+
+        if(!access_ok) {
+            snprintf(
+                ctx->app->state_string,
+                sizeof(ctx->app->state_string),
+                "Failed access 0x%08lX",
+                address);
+            swd_script_gui_refresh(ctx);
+        } else {
+            break;
+        }
+    }
+
+    if(!access_ok) {
+        notification_message_block(ctx->app->notification, &seq_error);
+        success = false;
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_dp_write(ScriptContext* ctx) {
+    uint32_t dp_bank = 0;
+    uint32_t dp_off = 0;
+    uint32_t data = 0;
+    bool success = true;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get data */
+    if(!swd_script_get_number(ctx, &data)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse data");
+        return false;
+    }
+
+    /* get dp_off */
+    if(!swd_script_get_number(ctx, &dp_off)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse DP offset");
+        return false;
+    }
+
+    /* get dp_bank */
+    if(!swd_script_get_number(ctx, &dp_bank)) {
+        dp_bank = 0xFF;
+    }
+
+    swd_script_log(
+        ctx, FuriLogLevelDefault, "write %08lX to reg %08lX / bank %08lX", data, dp_off, dp_bank);
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+    uint8_t ret = swd_write_dpbank(ctx->app, dp_off, dp_bank, &data);
+    if(ret != 1) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_write_dpbank failed");
+        success = false;
+    }
+
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_dp_read(ScriptContext* ctx) {
+    uint32_t dp_bank = 0;
+    uint32_t dp_off = 0;
+    uint32_t data = 0;
+    bool success = true;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get dp_off */
+    if(!swd_script_get_number(ctx, &dp_off)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse DP offset");
+        return false;
+    }
+
+    /* get dp_bank */
+    if(!swd_script_get_number(ctx, &dp_bank)) {
+        dp_bank = 0xFF;
+    }
+
+    swd_script_log(ctx, FuriLogLevelDefault, "read reg %02lX / bank %02lX", dp_off, dp_bank);
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+    uint8_t ret = swd_read_dpbank(ctx->app, dp_off, dp_bank, &data);
+    if(ret != 1) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_read_dpbank failed");
+        success = false;
+    } else {
+        swd_script_log(ctx, FuriLogLevelDefault, "result: 0x%08lX", data);
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_ap_write(ScriptContext* ctx) {
+    uint32_t ap_reg = 0;
+    uint32_t data = 0;
+    bool success = true;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get data */
+    if(!swd_script_get_number(ctx, &data)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse data");
+        return false;
+    }
+
+    /* get ap_reg */
+    if(!swd_script_get_number(ctx, &ap_reg)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse AP register");
+        return false;
+    }
+
+    swd_script_log(
+        ctx, FuriLogLevelDefault, "AP%d %08lX -> %02lX", ctx->selected_ap, data, ap_reg);
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+    uint8_t ret = swd_write_ap(ctx->app, ctx->selected_ap, ap_reg, data);
+    if(ret != 1) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_write_ap failed");
+        success = false;
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_ap_read(ScriptContext* ctx) {
+    uint32_t ap_reg = 0;
+    uint32_t data = 0;
+    bool success = true;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    /* get ap_reg */
+    if(!swd_script_get_number(ctx, &ap_reg)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse AP register");
+        return false;
+    }
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+    uint8_t ret = swd_read_ap(ctx->app, ctx->selected_ap, ap_reg, &data);
+    if(ret != 1) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_read_ap failed");
+        success = false;
+    } else {
+        swd_script_log(
+            ctx, FuriLogLevelDefault, "AP%d %02lX: %08lX", ctx->selected_ap, ap_reg, data);
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    swd_script_seek_newline(ctx);
+
+    return success;
+}
+
+static bool swd_scriptfunc_core_halt(ScriptContext* ctx) {
+    bool succ = false;
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+    uint32_t reg_dhcsr = SCS_DHCSR_KEY | SCS_DHCSR_C_HALT | SCS_DHCSR_C_DEBUGEN;
+
+    succ = swd_write_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, reg_dhcsr) == 1;
+
+    if(!succ) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed");
+    } else {
+        swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &reg_dhcsr);
+
+        if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) {
+            swd_script_log(ctx, FuriLogLevelError, "Core did not halt");
+            succ = false;
+        } else {
+            swd_script_log(ctx, FuriLogLevelDefault, "Core halted");
+        }
+    }
+
+    furi_mutex_release(ctx->app->swd_mutex);
+    swd_script_seek_newline(ctx);
+
+    return succ;
+}
+
+static bool swd_scriptfunc_core_continue(ScriptContext* ctx) {
+    bool succ = false;
+    uint32_t data = 0;
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+    succ = swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &data) == 1;
+
+    if(!(data & SCS_DHCSR_S_HALT)) {
+        swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state");
+        succ = false;
+    } else {
+        succ = swd_write_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, SCS_DHCSR_KEY) == 1;
+        furi_mutex_release(ctx->app->swd_mutex);
+    }
+
+    if(!succ) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed");
+    } else {
+        swd_script_log(ctx, FuriLogLevelDefault, "Core continued");
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return succ;
+}
+
+static bool swd_scriptfunc_core_step(ScriptContext* ctx) {
+    bool succ = false;
+    uint32_t data = 0;
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+    succ = swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &data) == 1;
+
+    if(!(data & SCS_DHCSR_S_HALT)) {
+        swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state");
+        succ = false;
+    } else {
+        succ = swd_write_memory(
+                   ctx->app,
+                   ctx->selected_ap,
+                   SCS_DHCSR,
+                   SCS_DHCSR_KEY | SCS_DHCSR_C_STEP | SCS_DHCSR_C_MASKINTS |
+                       SCS_DHCSR_C_DEBUGEN) == 1;
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    if(!succ) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed");
+    } else {
+        swd_script_log(ctx, FuriLogLevelDefault, "Core stepped");
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return succ;
+}
+
+static struct cpu_regs_type {
+    uint8_t regsel;
+    const char* desc;
+} cpu_regs[] = {
+    {0x00, "R00"},    {0x01, "R01"},    {0x02, "R02"},    {0x03, "R03"},    {0x04, "R04"},
+    {0x05, "R05"},    {0x06, "R06"},    {0x07, "R07"},    {0x08, "R08"},    {0x09, "R09"},
+    {0x0A, "R10"},    {0x0B, "R11"},    {0x0C, "R12"},    {0x0D, "SP/R13"}, {0x0E, "LR/R14"},
+    {0x0F, "PC/R15"}, {0x10, "xPSR"},   {0x11, "MSP"},    {0x12, "PSP"},    {0x14, "Flags"},
+    {0x21, "FPCSR"},  {0x40, "FP S00"}, {0x41, "FP S01"}, {0x42, "FP S02"}, {0x43, "FP S03"},
+    {0x44, "FP S04"}, {0x45, "FP S05"}, {0x46, "FP S06"}, {0x47, "FP S07"}, {0x48, "FP S08"},
+    {0x49, "FP S09"}, {0x4A, "FP S10"}, {0x4B, "FP S11"}, {0x4C, "FP S12"}, {0x4D, "FP S13"},
+    {0x4E, "FP S14"}, {0x4F, "FP S15"}, {0x50, "FP S16"}, {0x51, "FP S17"}, {0x52, "FP S18"},
+    {0x53, "FP S19"}, {0x54, "FP S20"}, {0x55, "FP S21"}, {0x56, "FP S22"}, {0x57, "FP S23"},
+    {0x58, "FP S24"}, {0x59, "FP S25"}, {0x5A, "FP S26"}, {0x5B, "FP S27"}, {0x5C, "FP S28"},
+    {0x5D, "FP S29"}, {0x5E, "FP S30"}, {0x5F, "FP S31"}};
+
+static bool swd_scriptfunc_core_regs(ScriptContext* ctx) {
+    bool succ = false;
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+
+    uint32_t reg_dhcsr = 0;
+    uint32_t reg_cpacr = 0;
+    swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &reg_dhcsr);
+    swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPACR, &reg_cpacr);
+
+    /* when FPU is enabled/available, CP10 and CP11 are implemented */
+    bool has_fpu = ((reg_cpacr >> 20) & 0x0F) != 0;
+
+    if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) {
+        swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state");
+        succ = false;
+    } else {
+        for(size_t pos = 0; pos < COUNT(cpu_regs); pos++) {
+            if(!has_fpu && (cpu_regs[pos].regsel >= 0x20)) {
+                continue;
+            }
+            uint32_t core_data = 0;
+            succ =
+                swd_write_memory(
+                    ctx->app, ctx->selected_ap, SCS_DCRSR, SCS_DCRSR_RD | cpu_regs[pos].regsel) ==
+                1;
+            succ &= swd_read_memory(ctx->app, ctx->selected_ap, SCS_DCRDR, &core_data) == 1;
+
+            if(!succ) {
+                swd_script_log(ctx, FuriLogLevelDefault, "%08s ----------", cpu_regs[pos].desc);
+            } else {
+                swd_script_log(
+                    ctx, FuriLogLevelDefault, "%06s 0x%08X", cpu_regs[pos].desc, core_data);
+            }
+        }
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    swd_script_seek_newline(ctx);
+
+    return true;
+}
+
+static bool swd_scriptfunc_core_reg_get(ScriptContext* ctx) {
+    uint32_t core_reg = 0;
+    uint32_t core_data = 0;
+    bool succ = false;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &core_reg)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse register");
+        return false;
+    }
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+    uint32_t reg_dhcsr = 0;
+    uint32_t reg_cpacr = 0;
+    swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &reg_dhcsr);
+    swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPACR, &reg_cpacr);
+
+    /* when FPU is enabled/available, CP10 and CP11 are implemented */
+    bool has_fpu = ((reg_cpacr >> 20) & 0x0F) != 0;
+
+    if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) {
+        swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state");
+        succ = false;
+    } else {
+        if(!has_fpu && (core_reg >= 0x20)) {
+            swd_script_log(ctx, FuriLogLevelError, "Core has no FP extensions");
+            succ = false;
+        } else {
+            succ = swd_write_memory(
+                       ctx->app, ctx->selected_ap, SCS_DCRSR, SCS_DCRSR_RD | core_reg) == 1;
+            succ &= swd_read_memory(ctx->app, ctx->selected_ap, SCS_DCRDR, &core_data) == 1;
+            if(!succ) {
+                swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed");
+            }
+        }
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    if(succ) {
+        swd_script_log(ctx, FuriLogLevelDefault, "0x%08X", core_data);
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return succ;
+}
+
+static bool swd_scriptfunc_core_reg_set(ScriptContext* ctx) {
+    uint32_t core_reg = 0;
+    uint32_t core_data = 0;
+    bool succ = false;
+
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+
+    if(!swd_script_get_number(ctx, &core_reg)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse register");
+        return false;
+    }
+    if(!swd_script_skip_whitespace(ctx)) {
+        swd_script_log(ctx, FuriLogLevelError, "missing whitespace");
+        return false;
+    }
+    if(!swd_script_get_number(ctx, &core_data)) {
+        swd_script_log(ctx, FuriLogLevelError, "failed to parse data");
+        return false;
+    }
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+    uint32_t reg_dhcsr = 0;
+    uint32_t reg_cpacr = 0;
+
+    swd_read_memory(ctx->app, ctx->selected_ap, SCS_DHCSR, &reg_dhcsr);
+    swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPACR, &reg_cpacr);
+
+    /* when FPU is enabled/available, CP10 and CP11 are implemented */
+    bool has_fpu = ((reg_cpacr >> 20) & 0x0F) != 0;
+
+    if(!(reg_dhcsr & SCS_DHCSR_S_HALT)) {
+        swd_script_log(ctx, FuriLogLevelError, "Core is not in debug state");
+        succ = false;
+    } else {
+        if(!has_fpu && (core_reg >= 0x20)) {
+            swd_script_log(ctx, FuriLogLevelError, "Core has no FP extensions");
+            succ = false;
+        } else {
+            succ = swd_write_memory(ctx->app, ctx->selected_ap, SCS_DCRDR, core_data) == 1;
+            succ &= swd_write_memory(
+                        ctx->app, ctx->selected_ap, SCS_DCRSR, SCS_DCRSR_WR | core_reg) == 1;
+            if(!succ) {
+                swd_script_log(ctx, FuriLogLevelError, "swd_write_memory failed");
+            }
+        }
+    }
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    swd_script_seek_newline(ctx);
+
+    return succ;
+}
+
+static bool swd_scriptfunc_core_cpuid(ScriptContext* ctx) {
+    bool succ = false;
+    uint32_t reg_cpuid = 0;
+
+    furi_mutex_acquire(ctx->app->swd_mutex, FuriWaitForever);
+    succ = swd_read_memory(ctx->app, ctx->selected_ap, SCS_CPUID, &reg_cpuid) == 1;
+    furi_mutex_release(ctx->app->swd_mutex);
+
+    if(!succ) {
+        swd_script_log(ctx, FuriLogLevelError, "swd_read_memory failed");
+    } else {
+        swd_script_log(ctx, FuriLogLevelDefault, "0x%08X", reg_cpuid);
+    }
+
+    swd_script_seek_newline(ctx);
+
+    return succ;
+}
+
+static const ScriptFunctionInfo script_funcs[] = {
+    {"#", &swd_scriptfunc_comment},
+    {".label", &swd_scriptfunc_label},
+    {"goto", &swd_scriptfunc_goto},
+    {"call", &swd_scriptfunc_call},
+    {"status", &swd_scriptfunc_status},
+    {"errors", &swd_scriptfunc_errors},
+    {"message", &swd_scriptfunc_message},
+    {"beep", &swd_scriptfunc_beep},
+    {"max_tries", &swd_scriptfunc_maxtries},
+    {"swd_clock_delay", &swd_scriptfunc_swd_clock_delay},
+    {"swd_idle_bits", &swd_scriptfunc_swd_idle_bits},
+    {"block_size", &swd_scriptfunc_blocksize},
+    {"abort", &swd_scriptfunc_abort},
+    {"mem_dump", &swd_scriptfunc_mem_dump},
+    {"mem_ldmst", &swd_scriptfunc_mem_ldmst},
+    {"mem_write", &swd_scriptfunc_mem_write},
+    {"mem_read", &swd_scriptfunc_mem_read},
+    {"dp_write", &swd_scriptfunc_dp_write},
+    {"dp_read", &swd_scriptfunc_dp_read},
+    {"ap_scan", &swd_scriptfunc_apscan},
+    {"ap_select", &swd_scriptfunc_apselect},
+    {"ap_read", &swd_scriptfunc_ap_read},
+    {"ap_write", &swd_scriptfunc_ap_write},
+    {"core_halt", &swd_scriptfunc_core_halt},
+    {"core_step", &swd_scriptfunc_core_step},
+    {"core_continue", &swd_scriptfunc_core_continue},
+    {"core_regs", &swd_scriptfunc_core_regs},
+    {"core_reg_get", &swd_scriptfunc_core_reg_get},
+    {"core_reg_set", &swd_scriptfunc_core_reg_set},
+    {"core_cpuid", &swd_scriptfunc_core_cpuid}};
+
+/************************** script main code **************************/
+
+static bool swd_execute_script_line(ScriptContext* const ctx) {
+    char buffer[64];
+    uint64_t start_pos = 0;
+
+    if(ctx->script_file) {
+        start_pos = storage_file_tell(ctx->script_file);
+        uint16_t ret = storage_file_read(ctx->script_file, buffer, 2);
+        storage_file_seek(ctx->script_file, start_pos, true);
+
+        if(ret < 2) {
+            return true;
+        }
+    } else {
+        start_pos = ctx->line_pos;
+        strncpy(buffer, ctx->line_data, 2);
+
+        if(buffer[0] == 0 || buffer[1] == 0) {
+            return true;
+        }
+    }
+
+    if(buffer[0] == '\n' || (buffer[0] == '\r' && buffer[1] == '\n')) {
+        swd_script_seek_newline(ctx);
+        return true;
+    }
+
+    for(size_t entry = 0; entry < COUNT(script_funcs); entry++) {
+        if(ctx->abort) {
+            DBGS("aborting");
+            break;
+        }
+        size_t expected = strlen(script_funcs[entry].prefix);
+
+        if(ctx->script_file) {
+            storage_file_seek(ctx->script_file, start_pos, true);
+
+            if(storage_file_read(ctx->script_file, buffer, expected) != expected) {
+                continue;
+            }
+        } else {
+            ctx->line_pos = start_pos;
+
+            if(strlen(ctx->line_data) < expected) {
+                continue;
+            }
+            strncpy(buffer, ctx->line_data, expected);
+            ctx->line_pos += expected;
+        }
+
+        buffer[expected] = '\000';
+        if(strncmp(buffer, script_funcs[entry].prefix, expected)) {
+            continue;
+        }
+        bool success = true;
+
+        if(ctx->goto_active) {
+            DBG("ignore: '%s'", script_funcs[entry].prefix);
+
+            /* only execute label handlers */
+            if(buffer[0] == '.') {
+                success = script_funcs[entry].func(ctx);
+            } else {
+                swd_script_seek_newline(ctx);
+            }
+        } else {
+            DBG("command: '%s'", script_funcs[entry].prefix);
+
+            if(!ctx->status_ignore) {
+                snprintf(
+                    ctx->app->state_string,
+                    sizeof(ctx->app->state_string),
+                    "CMD: %s",
+                    script_funcs[entry].prefix);
+            }
+            swd_script_gui_refresh(ctx);
+
+            /* function, execute */
+            success = script_funcs[entry].func(ctx);
+
+            if(!success && !ctx->errors_ignore) {
+                swd_script_log(
+                    ctx, FuriLogLevelError, "Command failed: %s", script_funcs[entry].prefix);
+                snprintf(
+                    ctx->app->state_string,
+                    sizeof(ctx->app->state_string),
+                    "Command failed: %s",
+                    script_funcs[entry].prefix);
+                return false;
+            }
+        }
+
+        return true;
+    }
+    swd_script_log(ctx, FuriLogLevelError, "unknown command '%s'", buffer);
+
+    return false;
+}
+
+static bool swd_execute_script(AppFSM* const ctx, const char* filename) {
+    bool success = true;
+
+    /* fetch current script and set as parent */
+    ScriptContext* parent = ctx->script;
+
+    ctx->script = malloc(sizeof(ScriptContext));
+    ctx->script->app = ctx;
+    ctx->script->max_tries = 1;
+    ctx->script->parent = parent;
+    strcpy(ctx->script->filename, filename);
+
+    if(!storage_file_exists(ctx->storage, filename)) {
+        DBG("Does not exist '%s'", filename);
+        parent = ctx->script->parent;
+        free(ctx->script);
+        ctx->script = parent;
+        return false;
+    }
+
+    /* first allocate a file object */
+    ctx->script->script_file = storage_file_alloc(ctx->storage);
+
+    /* then get our script opened */
+    if(!storage_file_open(ctx->script->script_file, filename, FSAM_READ, FSOM_OPEN_EXISTING)) {
+        FURI_LOG_E(TAG, "open, %s", storage_file_get_error_desc(ctx->script->script_file));
+        DBG("Failed to open '%s'", filename);
+        storage_file_free(ctx->script->script_file);
+        parent = ctx->script->parent;
+        free(ctx->script);
+        ctx->script = parent;
+        return false;
+    }
+
+    do {
+        success = true;
+        ctx->script->restart = false;
+
+        storage_file_seek(ctx->script->script_file, 0, true);
+
+        uint32_t line = 1;
+        while(line < SCRIPT_MAX_LINES) {
+            if(ctx->script->abort) {
+                DBGS("Abort requested");
+                break;
+            }
+            if(storage_file_eof(ctx->script->script_file)) {
+                break;
+            }
+            DBG("line %lu", line);
+            if(!swd_execute_script_line(ctx->script)) {
+                success = false;
+                break;
+            }
+            if(ctx->script->restart) {
+                break;
+            }
+            line++;
+        }
+
+        if(ctx->script->restart) {
+            DBGS("Restarting");
+        } else {
+            DBGS("Finished");
+        }
+
+        if(line >= SCRIPT_MAX_LINES) {
+            success = true;
+            char text_buf[128];
+
+            snprintf(text_buf, sizeof(text_buf), "aborting after %d lines", SCRIPT_MAX_LINES);
+            DialogMessage* message = dialog_message_alloc();
+            dialog_message_set_header(message, "SWD Probe", 16, 2, AlignLeft, AlignTop);
+            dialog_message_set_icon(message, &I_app, 3, 2);
+            dialog_message_set_text(message, text_buf, 3, 16, AlignLeft, AlignTop);
+            dialog_message_set_buttons(message, "Back", NULL, NULL);
+            dialog_message_free(message);
+
+            ctx->script->restart = false;
+        }
+
+        if(!success) {
+            char text_buf[128];
+
+            snprintf(text_buf, sizeof(text_buf), "Line %lu failed:\n%s", line, ctx->state_string);
+            DialogMessage* message = dialog_message_alloc();
+            dialog_message_set_header(message, "SWD Probe", 16, 2, AlignLeft, AlignTop);
+            dialog_message_set_icon(message, &I_app, 3, 2);
+            dialog_message_set_text(message, text_buf, 3, 16, AlignLeft, AlignTop);
+            dialog_message_set_buttons(message, "Back", "Retry", NULL);
+            if(dialog_message_show(ctx->dialogs, message) == DialogMessageButtonCenter) {
+                ctx->script->restart = true;
+            }
+            dialog_message_free(message);
+        }
+    } while(ctx->script->restart);
+
+    storage_file_close(ctx->script->script_file);
+    storage_file_free(ctx->script->script_file);
+
+    parent = ctx->script->parent;
+    free(ctx->script);
+    ctx->script = parent;
+
+    return success;
+}
+
+/************************** UI functions **************************/
+
+#define CANVAS_WIDTH 128
+#define CANVAS_HEIGHT 64
+
+#define COERCE(d, min, max) \
+    do {                    \
+        if(d < (min)) {     \
+            d = (min);      \
+        }                   \
+        if(d > (max)) {     \
+            d = (max);      \
+        }                   \
+    } while(0)
+
+#define COERCE_COORDS(x1, y1, x2, y2) \
+    do {                              \
+        COERCE(x1, 0, CANVAS_WIDTH);  \
+        COERCE(x2, 0, CANVAS_WIDTH);  \
+        COERCE(y1, 0, CANVAS_HEIGHT); \
+        COERCE(y1, 0, CANVAS_HEIGHT); \
+    } while(0)
+
+#include "model/model_chip.h"
+
+static int rotatedVertexCoords[NUM_VERTICES][3];
+
+static void draw_model(Canvas* const canvas) {
+    static float xAngle = 0;
+    static float yAngle = 0;
+    static float zAngle = 0;
+    static float zoom = 0;
+    static float speed = 0.6f;
+
+    float cosXAngle = cosf(xAngle);
+    float sinXAngle = sinf(xAngle);
+    float cosYAngle = cosf(yAngle);
+    float sinYAngle = sinf(yAngle);
+    float cosZAngle = cosf(zAngle);
+    float sinZAngle = sinf(zAngle);
+    float sinZoom = 1.2f + sinf(zoom) * 0.25f;
+
+    int centerX = CANVAS_WIDTH / 2;
+    int centerY = CANVAS_HEIGHT / 2 + 5;
+
+    for(int i = 0; i < NUM_VERTICES; i++) {
+        int x = vertexCoords[i][0] * sinZoom * 16;
+        int y = vertexCoords[i][1] * sinZoom * 16;
+        int z = vertexCoords[i][2] * sinZoom * 16;
+
+        int y1 = y * cosXAngle - z * sinXAngle;
+        int z1 = y * sinXAngle + z * cosXAngle;
+
+        int x2 = x * cosYAngle + z1 * sinYAngle;
+        int z2 = -x * sinYAngle + z1 * cosYAngle;
+
+        int x3 = x2 * cosZAngle - y1 * sinZAngle;
+        int y3 = x2 * sinZAngle + y1 * cosZAngle;
+
+        rotatedVertexCoords[i][0] = x3 + centerX;
+        rotatedVertexCoords[i][1] = y3 + centerY;
+        rotatedVertexCoords[i][2] = z2;
+    }
+
+    for(size_t i = 0; i < COUNT(edgeIndices); i++) {
+        int v1Index = edgeIndices[i][0];
+        int v2Index = edgeIndices[i][1];
+        int x1 = rotatedVertexCoords[v1Index][0];
+        int y1 = rotatedVertexCoords[v1Index][1];
+        int x2 = rotatedVertexCoords[v2Index][0];
+        int y2 = rotatedVertexCoords[v2Index][1];
+
+        COERCE_COORDS(x1, y1, x2, y2);
+        canvas_draw_line(canvas, x1, y1, x2, y2);
+    }
+
+    xAngle += speed * 0.02 / sinZoom;
+    yAngle += speed * 0.023 / sinZoom;
+    zAngle += speed * 0.029 * sinZoom;
+    zoom += speed * 0.005;
+}
+
+static void render_callback(Canvas* const canvas, void* ctx_in) {
+    furi_assert(canvas);
+    furi_assert(ctx_in);
+
+    AppFSM* ctx = ctx_in;
+    furi_mutex_acquire(ctx->gui_mutex, FuriWaitForever);
+
+    char buffer[64];
+    int y = 10;
+
+    canvas_draw_frame(canvas, 0, 0, 128, 64);
+    canvas_set_font(canvas, FontPrimary);
+
+    if(!ctx->detected_device) {
+        ctx->mode_page = ModePageScan;
+    } else if(ctx->mode_page == ModePageScan) {
+        ctx->mode_page = ModePageFound;
+    }
+
+    /* if seen less than a quarter second ago */
+    switch(ctx->mode_page) {
+    case ModePageScan: {
+        draw_model(canvas);
+
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Searching");
+        y += 14;
+
+        canvas_set_font(canvas, FontSecondary);
+
+        bool info_page = (ctx->loop_count % 500) >= 250;
+        if(info_page) {
+            canvas_draw_str(canvas, 2, y, "Connect GND with target GND");
+            y += 10;
+            canvas_draw_str(canvas, 2, y, "and any two GPIOs with pads");
+            y += 10;
+            canvas_draw_str(canvas, 2, y, "you want to check for SWD");
+
+            canvas_set_font(canvas, FontPrimary);
+            canvas_draw_str(canvas, 111, 62, "2/2");
+        } else {
+            const char* filename = "<none>";
+            if(strlen(ctx->script_detected) > 0) {
+                const char* slash = strrchr(ctx->script_detected, '/');
+                if(slash) {
+                    filename = &slash[1];
+                } else {
+                    filename = ctx->script_detected;
+                }
+            }
+
+            canvas_set_font(canvas, FontSecondary);
+            canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Autoexec Script");
+            y += 10;
+            canvas_set_font(canvas, FontKeyboard);
+            canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, filename);
+            y += 16;
+
+            canvas_set_font(canvas, FontSecondary);
+            canvas_draw_icon(canvas, 14, y - 5, &I_ButtonUp_7x4);
+            canvas_draw_icon(canvas, 78, y - 5, &I_ButtonDown_7x4);
+            canvas_draw_str(canvas, 23, y, "Clear");
+            canvas_draw_str(canvas, 87, y, "Choose");
+
+            canvas_set_font(canvas, FontPrimary);
+            canvas_draw_str(canvas, 111, 62, "1/2");
+        }
+        canvas_set_font(canvas, FontSecondary);
+        elements_button_left(canvas, "Script");
+        break;
+    }
+    case ModePageFound: {
+        if((ctx->detected_timeout + TIMER_HZ / 4) >= TIMER_HZ * TIMEOUT) {
+            snprintf(buffer, sizeof(buffer), "FOUND!");
+        } else {
+            /* if it was seen more than a quarter second ago, show countdown */
+            snprintf(
+                buffer, sizeof(buffer), "FOUND! (%lus)", (ctx->detected_timeout / TIMER_HZ) + 1);
+        }
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, buffer);
+        y += 10;
+        canvas_set_font(canvas, FontKeyboard);
+
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "SWC/SWD: %s/%s",
+            gpio_name(ctx->io_swc),
+            gpio_name(ctx->io_swd));
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+        snprintf(buffer, sizeof(buffer), "DPIDR 0x%08lX", ctx->dp_regs.dpidr);
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+
+        snprintf(
+            buffer,
+            sizeof(buffer),
+            "Part %02X Rev %X DAPv%d",
+            ctx->dpidr_info.partno,
+            ctx->dpidr_info.revision,
+            ctx->dpidr_info.version);
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+
+        canvas_set_font(canvas, FontSecondary);
+        snprintf(buffer, sizeof(buffer), "%s", jep106_manufacturer(ctx->dpidr_info.designer));
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+
+        canvas_set_font(canvas, FontSecondary);
+        elements_button_left(canvas, "Script");
+        elements_button_right(canvas, "DP Regs");
+
+        break;
+    }
+    case ModePageDPRegs: {
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP Registers");
+        y += 10;
+        canvas_set_font(canvas, FontKeyboard);
+        if(ctx->dp_regs.dpidr_ok) {
+            snprintf(buffer, sizeof(buffer), "DPIDR %08lX", ctx->dp_regs.dpidr);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        }
+        y += 10;
+
+        if(ctx->dp_regs.ctrlstat_ok) {
+            snprintf(buffer, sizeof(buffer), "CTRL  %08lX", ctx->dp_regs.ctrlstat);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        }
+        y += 10;
+
+        if(ctx->dp_regs.targetid_ok) {
+            snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        }
+        y += 10;
+
+        if(ctx->dp_regs.eventstat_ok) {
+            snprintf(buffer, sizeof(buffer), "EVTST %08lX", ctx->dp_regs.eventstat);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        }
+        y += 10;
+        canvas_set_font(canvas, FontSecondary);
+        elements_button_left(canvas, "Scan");
+        elements_button_right(canvas, "DPID");
+
+        break;
+    }
+
+    case ModePageDPID: {
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "DP ID Register");
+        y += 10;
+        canvas_set_font(canvas, FontKeyboard);
+        if(ctx->dpidr_info.version != 2) {
+            snprintf(buffer, sizeof(buffer), "TARGETID not supported");
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            y += 10;
+        } else {
+            if(ctx->dp_regs.targetid_ok) {
+                snprintf(buffer, sizeof(buffer), "TGTID %08lX", ctx->dp_regs.targetid);
+                canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+                y += 10;
+
+                snprintf(buffer, sizeof(buffer), "Part No. %04X", ctx->targetid_info.partno);
+                canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+                y += 10;
+                snprintf(
+                    buffer, sizeof(buffer), "%s", jep106_manufacturer(ctx->targetid_info.designer));
+                canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+                y += 10;
+            }
+        }
+        canvas_set_font(canvas, FontSecondary);
+        elements_button_left(canvas, "DP Regs");
+        elements_button_right(canvas, "APs");
+        break;
+    }
+
+    case ModePageAPID: {
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "AP Menu");
+        y += 10;
+        canvas_set_font(canvas, FontKeyboard);
+
+        char state = ' ';
+        if(ctx->ap_pos >= ctx->ap_scanned && ctx->ap_pos <= ctx->ap_scanned + 10) {
+            state = '*';
+        }
+
+        if(!ctx->apidr_info[ctx->ap_pos].ok) {
+            snprintf(buffer, sizeof(buffer), "[%d]%c<none>", ctx->ap_pos, state);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            y += 10;
+
+            if(ctx->ap_pos == 0) {
+                for(size_t pos = 0; pos < COUNT(ctx->apidr_info); pos++) {
+                    if(ctx->apidr_info[pos].ok) {
+                        ctx->ap_pos = pos;
+                    }
+                }
+            }
+        } else {
+            const char* class = "";
+
+            switch(ctx->apidr_info[ctx->ap_pos].class) {
+            case 0:
+                class = "und";
+                break;
+            case 1:
+                class = "COM";
+                break;
+            case 8:
+                class = "MEM";
+                break;
+            default:
+                class = "unk";
+                break;
+            }
+
+            const char* types[] = {
+                "COM-AP",
+                "AHB3",
+                "APB2 or APB3",
+                "Type unknown",
+                "AXI3 or AXI4",
+                "AHB5",
+                "APB4 and APB5",
+                "AXI5",
+                "AHB5 enh.",
+            };
+            const char* type = "Type unk";
+
+            if(ctx->apidr_info[ctx->ap_pos].type < COUNT(types)) {
+                type = types[ctx->apidr_info[ctx->ap_pos].type];
+            }
+
+            snprintf(buffer, sizeof(buffer), "[%d]%c%s, %s", ctx->ap_pos, state, class, type);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            y += 10;
+
+            snprintf(buffer, sizeof(buffer), "Base 0x%08lX", ctx->apidr_info[ctx->ap_pos].base);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            y += 10;
+
+            snprintf(
+                buffer,
+                sizeof(buffer),
+                "Rev %d Var %d",
+                ctx->apidr_info[ctx->ap_pos].revision,
+                ctx->apidr_info[ctx->ap_pos].variant);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            y += 10;
+
+            snprintf(
+                buffer,
+                sizeof(buffer),
+                "%s",
+                jep106_manufacturer(ctx->apidr_info[ctx->ap_pos].designer));
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            y += 10;
+
+            elements_button_center(canvas, "Show");
+        }
+        canvas_set_font(canvas, FontSecondary);
+        elements_button_left(canvas, "DPID");
+        elements_button_right(canvas, "CoreS.");
+        elements_scrollbar_pos(canvas, 4, 10, 40, ctx->ap_pos / 32, COUNT(ctx->apidr_info) / 32);
+        break;
+    }
+
+    /* hex dump view */
+    case ModePageHexDump: {
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Hex dump");
+        y += 10;
+        canvas_set_font(canvas, FontKeyboard);
+
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, "Addr:");
+
+        snprintf(buffer, sizeof(buffer), "%08lX", ctx->hex_addr);
+        canvas_draw_str_aligned(canvas, 38, y, AlignLeft, AlignBottom, buffer);
+        uint32_t font_width = canvas_glyph_width(canvas, '0');
+        uint32_t x = 37 + (7 - ctx->hex_select) * font_width;
+
+        /* draw selection */
+        canvas_draw_line(canvas, x, y + 1, x + font_width, y + 1);
+        y += 10;
+
+        uint32_t byte_num = 0;
+        for(int line = 0; line < 4; line++) {
+            uint32_t x_pos = 5;
+
+            for(int byte_pos = 0; byte_pos < 8; byte_pos++) {
+                if(ctx->hex_buffer_valid[byte_num / 4]) {
+                    snprintf(buffer, sizeof(buffer), "%02X", ctx->hex_buffer[byte_num]);
+                } else {
+                    snprintf(buffer, sizeof(buffer), "--");
+                }
+                byte_num++;
+                canvas_draw_str_aligned(canvas, x_pos, y, AlignLeft, AlignBottom, buffer);
+                x_pos += font_width * 2 + font_width / 2;
+            }
+            y += 10;
+        }
+        break;
+    }
+
+    case ModePageCoresight: {
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Coresight");
+        y += 10;
+        canvas_set_font(canvas, FontSecondary);
+
+        uint32_t base = ctx->coresight_bases[ctx->coresight_level];
+        uint32_t base_next = adi_romtable_get(ctx, base, ctx->coresight_pos[ctx->coresight_level]);
+
+        snprintf(buffer, sizeof(buffer), "Base: %08lX", base);
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+        snprintf(buffer, sizeof(buffer), "Type: %s", adi_romtable_type(ctx, base));
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+        snprintf(buffer, sizeof(buffer), "Full: %s", adi_romtable_full(ctx, base));
+        canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+        y += 10;
+
+        if(adi_is_romtable(ctx, base)) {
+            snprintf(
+                buffer,
+                sizeof(buffer),
+                "[%lu/%lu] -> %08lX",
+                ctx->coresight_pos[ctx->coresight_level] + 1,
+                ctx->coresight_count[ctx->coresight_level],
+                base_next);
+            canvas_draw_str_aligned(canvas, 5, y, AlignLeft, AlignBottom, buffer);
+            canvas_set_font(canvas, FontSecondary);
+            elements_button_center(canvas, "Enter");
+        }
+        y += 10;
+
+        canvas_set_font(canvas, FontSecondary);
+
+        if(ctx->coresight_level) {
+            elements_button_left(canvas, "Prev");
+        } else {
+            elements_button_left(canvas, "APs");
+        }
+        elements_scrollbar_pos(
+            canvas,
+            4,
+            10,
+            40,
+            ctx->coresight_pos[ctx->coresight_level],
+            ctx->coresight_count[ctx->coresight_level]);
+
+        break;
+    }
+
+    /* hex dump view */
+    case ModePageScript: {
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, "Script");
+        y += 10;
+        y += 10;
+        canvas_draw_str_aligned(canvas, 10, y, AlignLeft, AlignBottom, "Status:");
+        y += 10;
+        canvas_set_font(canvas, FontKeyboard);
+        canvas_draw_str_aligned(canvas, 64, y, AlignCenter, AlignBottom, ctx->state_string);
+        y += 10;
+        break;
+    }
+    }
+
+    furi_mutex_release(ctx->gui_mutex);
+}
+
+static void input_callback(InputEvent* input_event, void* ctx_in) {
+    furi_assert(input_event);
+    furi_assert(ctx_in);
+    AppFSM* ctx = ctx_in;
+
+    int entries = furi_message_queue_get_count(ctx->event_queue);
+
+    /* better skip than sorry */
+    if(entries < QUEUE_SIZE) {
+        AppEvent event = {.type = EventKeyPress, .input = *input_event};
+        furi_message_queue_put(ctx->event_queue, &event, 0);
+    }
+}
+
+static void app_init(AppFSM* const app) {
+    furi_assert(app);
+
+    app->loop_count = 0;
+    app->current_mask_id = 0;
+    app->current_mask = gpio_direction_mask[app->current_mask_id];
+    app->io_swd = 0xFF;
+    app->io_swc = 0xFF;
+    app->hex_addr = 0x40002800;
+    app->hex_addr = 0xE000EDF0;
+    app->swd_clock_delay = CLOCK_DELAY;
+    app->swd_idle_bits = IDLE_BITS;
+
+    strcpy(app->state_string, "none");
+    strcpy(app->script_detected, "");
+}
+
+static void app_deinit(AppFSM* const app) {
+    furi_assert(app);
+
+    strcpy(app->state_string, "exiting");
+}
+
+static void swd_main_loop(AppFSM* ctx) {
+    furi_assert(ctx);
+
+    ctx->loop_count++;
+
+    switch(ctx->mode_page) {
+    case ModePageScan:
+    case ModePageFound: {
+        /* reset after timeout */
+        if(ctx->detected_timeout > 0) {
+            ctx->detected_timeout--;
+        } else {
+            DBGS("Reset detected flag");
+            ctx->detected_device = false;
+            ctx->io_swd = 0xFF;
+            ctx->io_swc = 0xFF;
+            ctx->io_num_swd = 0xFF;
+            ctx->io_num_swc = 0xFF;
+            ctx->ap_scanned = 0;
+            memset(&ctx->dp_regs, 0x00, sizeof(ctx->dp_regs));
+            memset(&ctx->targetid_info, 0x00, sizeof(ctx->targetid_info));
+            memset(&ctx->apidr_info, 0x00, sizeof(ctx->apidr_info));
+            ctx->script_detected_executed = false;
+        }
+
+        ctx->detected = false;
+        ctx->current_mask = gpio_direction_mask[ctx->current_mask_id];
+
+        /* when SWD was already detected, set it to data pin regardless of the mask */
+        if(ctx->detected_device) {
+            ctx->current_mask &= ~ctx->io_swd;
+        }
+
+        /* do the scan */
+        furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+        swd_scan(ctx);
+        furi_mutex_release(ctx->swd_mutex);
+
+        /* now when detected a device, set the timeout */
+        if(ctx->detected) {
+            DBGS("Set detected flag");
+            ctx->detected_device = true;
+            ctx->detected_timeout = TIMER_HZ * TIMEOUT;
+
+            /* update DPIDR fields */
+            ctx->dpidr_info.revision = (ctx->dp_regs.dpidr >> 28) & 0x0F;
+            ctx->dpidr_info.partno = (ctx->dp_regs.dpidr >> 20) & 0xFF;
+            ctx->dpidr_info.version = (ctx->dp_regs.dpidr >> 12) & 0x0F;
+            ctx->dpidr_info.designer = (ctx->dp_regs.dpidr >> 1) & 0x3FF;
+
+            if(!has_multiple_bits(ctx->io_swc)) {
+                DBGS(" - Detected pins");
+                DBGS(" - Resetting error");
+
+                furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+                /* reset error */
+                /* first make sure we have the correct bank by invalidating the current select cache */
+                ctx->dp_regs.select_ok = false;
+                uint8_t ack =
+                    swd_read_dpbank(ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat);
+
+                if(ack != 1 || (ctx->dp_regs.ctrlstat & STAT_ERROR_FLAGS)) {
+                    DBGS(" - send ABORT");
+                    swd_abort(ctx);
+                }
+                DBGS(" - Fetch CTRL/STAT");
+                ctx->dp_regs.ctrlstat_ok =
+                    swd_read_dpbank(
+                        ctx, REG_CTRLSTAT, REG_CTRLSTAT_BANK, &ctx->dp_regs.ctrlstat) == 1;
+                DBG("     %08lX %s",
+                    ctx->dp_regs.ctrlstat,
+                    ctx->dp_regs.ctrlstat_ok ? "OK" : "FAIL");
+
+                if(ctx->dpidr_info.version >= 1) {
+                    DBGS(" - DAPv1, read DLCR");
+                    ctx->dp_regs.dlcr_ok =
+                        swd_read_dpbank(ctx, REG_DLCR, REG_DLCR_BANK, &ctx->dp_regs.dlcr) == 1;
+                    DBG("     %08lX %s", ctx->dp_regs.dlcr, ctx->dp_regs.dlcr_ok ? "OK" : "FAIL");
+                }
+
+                if(ctx->dpidr_info.version >= 2) {
+                    DBGS(" - DAPv2, read TARGETID");
+                    ctx->dp_regs.targetid_ok =
+                        swd_read_dpbank(
+                            ctx, REG_TARGETID, REG_TARGETID_BANK, &ctx->dp_regs.targetid) == 1;
+                    DBG("     %08lX %s",
+                        ctx->dp_regs.targetid,
+                        ctx->dp_regs.targetid_ok ? "OK" : "FAIL");
+                    DBGS(" - DAPv2, read EVENTSTAT");
+                    ctx->dp_regs.eventstat_ok =
+                        swd_read_dpbank(
+                            ctx, REG_EVENTSTAT, REG_EVENTSTAT_BANK, &ctx->dp_regs.eventstat) == 1;
+                    DBG("     %08lX %s",
+                        ctx->dp_regs.eventstat,
+                        ctx->dp_regs.eventstat_ok ? "OK" : "FAIL");
+                    DBGS(" - DAPv2, read DLPIDR");
+                    ctx->dp_regs.dlpidr_ok =
+                        swd_read_dpbank(ctx, REG_DLPIDR, REG_DLPIDR_BANK, &ctx->dp_regs.dlpidr) ==
+                        1;
+                    DBG("     %08lX %s",
+                        ctx->dp_regs.dlpidr,
+                        ctx->dp_regs.dlpidr_ok ? "OK" : "FAIL");
+                }
+
+                if(ctx->dp_regs.targetid_ok) {
+                    ctx->targetid_info.revision = (ctx->dp_regs.targetid >> 28) & 0x0F;
+                    ctx->targetid_info.partno = (ctx->dp_regs.targetid >> 12) & 0xFFFF;
+                    ctx->targetid_info.designer = (ctx->dp_regs.targetid >> 1) & 0x3FF;
+                }
+
+                if(!ctx->script_detected_executed && strlen(ctx->script_detected) > 0) {
+                    DBG(" - Run script '%s'", ctx->script_detected);
+
+                    ctx->script_detected_executed = true;
+
+                    ctx->mode_page = ModePageScript;
+                    swd_execute_script(ctx, ctx->script_detected);
+                    ctx->mode_page = ModePageFound;
+                }
+                furi_mutex_release(ctx->swd_mutex);
+            }
+        } else {
+            if(!has_multiple_bits(ctx->io_swc)) {
+                DBGS(" - Lost device");
+            }
+        }
+
+        ctx->current_mask_id = (ctx->current_mask_id + 1) % COUNT(gpio_direction_mask);
+        break;
+    }
+
+    case ModePageDPRegs:
+    case ModePageAPID: {
+        furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+        /* set debug enable request */
+        if(!swd_ensure_powerup(ctx)) {
+            furi_mutex_release(ctx->swd_mutex);
+            break;
+        }
+
+        /* only scan a few APs at once to stay responsive */
+        for(int pos = 0; pos < 8; pos++) {
+            if(ctx->ap_scanned == 0) {
+                swd_apscan_reset(ctx);
+            }
+
+            uint8_t ap = ctx->ap_scanned++;
+
+            if(ctx->apidr_info[ap].tested) {
+                continue;
+            }
+            if(swd_apscan_test(ctx, ap)) {
+                break;
+            }
+        }
+        furi_mutex_release(ctx->swd_mutex);
+        break;
+    }
+
+    case ModePageHexDump: {
+        furi_mutex_acquire(ctx->swd_mutex, FuriWaitForever);
+
+        for(size_t byte_pos = 0; byte_pos < sizeof(ctx->hex_buffer); byte_pos += 4) {
+            uint32_t* data = (uint32_t*)&ctx->hex_buffer[byte_pos];
+            bool ret = swd_read_memory(ctx, ctx->ap_pos, ctx->hex_addr + byte_pos, data) == 1;
+
+            ctx->hex_buffer_valid[byte_pos / 4] = ret;
+
+            if(!ret) {
+                swd_abort_simple(ctx);
+            }
+        }
+        furi_mutex_release(ctx->swd_mutex);
+        break;
+    }
+
+    case ModePageDPID:
+    case ModePageCoresight:
+        furi_delay_ms(50);
+        break;
+    }
+}
+
+static bool swd_message_process(AppFSM* ctx) {
+    bool processing = true;
+    AppEvent event;
+
+    /* wait to make sure the OS can do its stuff */
+    FuriStatus event_status = furi_message_queue_get(ctx->event_queue, &event, 1000 / TIMER_HZ);
+
+    if(event_status != FuriStatusOk) {
+        return processing;
+    }
+
+    if(event.type == EventKeyPress) {
+        if(event.input.type == InputTypePress) {
+            switch(event.input.key) {
+            case InputKeyUp:
+                switch(ctx->mode_page) {
+                default:
+                    break;
+
+                case ModePageScan:
+                case ModePageFound: {
+                    strcpy(ctx->script_detected, "");
+                    break;
+                }
+
+                case ModePageAPID:
+                    if(ctx->ap_pos > 0) {
+                        ctx->ap_pos--;
+                    }
+                    break;
+
+                case ModePageHexDump: {
+                    ctx->hex_addr += ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select));
+                    break;
+                }
+
+                case ModePageCoresight: {
+                    if(ctx->coresight_pos[ctx->coresight_level] > 0) {
+                        ctx->coresight_pos[ctx->coresight_level]--;
+                    }
+                    break;
+                }
+                }
+                break;
+
+            case InputKeyDown: {
+                switch(ctx->mode_page) {
+                default:
+                    break;
+
+                case ModePageScan: {
+                    FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts"));
+                    FuriString* preselected = furi_string_alloc_printf(
+                        (strlen(ctx->script_detected) > 0) ? ctx->script_detected :
+                                                             ANY_PATH("swd_scripts"));
+                    DialogsFileBrowserOptions options;
+
+                    dialog_file_browser_set_basic_options(&options, "swd", &I_swd);
+
+                    if(dialog_file_browser_show(ctx->dialogs, result_path, preselected, &options)) {
+                        const char* path = furi_string_get_cstr(result_path);
+                        strcpy(ctx->script_detected, path);
+                    }
+
+                    furi_string_free(result_path);
+                    furi_string_free(preselected);
+                    break;
+                }
+
+                case ModePageAPID:
+                    if(ctx->ap_pos + 1U < COUNT(ctx->apidr_info)) {
+                        ctx->ap_pos++;
+                    }
+                    break;
+
+                case ModePageHexDump: {
+                    ctx->hex_addr -= ((ctx->hex_select) ? 1 : 8) * (1 << (4 * ctx->hex_select));
+                    break;
+                }
+
+                case ModePageCoresight: {
+                    if(ctx->coresight_pos[ctx->coresight_level] + 1 <
+                       ctx->coresight_count[ctx->coresight_level]) {
+                        ctx->coresight_pos[ctx->coresight_level]++;
+                    }
+                    break;
+                }
+                }
+                break;
+            }
+
+            case InputKeyRight:
+                if(ctx->mode_page == ModePageHexDump) {
+                    if(ctx->hex_select > 0) {
+                        ctx->hex_select--;
+                    }
+                } else if(ctx->mode_page == ModePageAPID && ctx->apidr_info[ctx->ap_pos].ok) {
+                    ctx->mode_page = ModePageCoresight;
+                    uint32_t base = ctx->apidr_info[ctx->ap_pos].base & 0xFFFFF000;
+                    ctx->coresight_level = 0;
+                    ctx->coresight_bases[ctx->coresight_level] = base;
+                    ctx->coresight_pos[ctx->coresight_level] = 0;
+                    ctx->coresight_count[ctx->coresight_level] =
+                        adi_romtable_entry_count(ctx, base);
+                } else if(ctx->detected) {
+                    if(ctx->mode_page + 1 < ModePageCount) {
+                        ctx->mode_page++;
+                    }
+                }
+                break;
+
+            case InputKeyLeft:
+                if(ctx->mode_page == ModePageHexDump) {
+                    if(ctx->hex_select < 7) {
+                        ctx->hex_select++;
+                    }
+                } else if(ctx->mode_page == ModePageCoresight) {
+                    if(ctx->coresight_level > 0) {
+                        ctx->coresight_level--;
+                    } else {
+                        ctx->mode_page = ModePageAPID;
+                    }
+                } else if((ctx->mode_page == ModePageScan) || (ctx->mode_page == ModePageFound)) {
+                    uint32_t mode_page = ctx->mode_page;
+                    FuriString* result_path = furi_string_alloc_printf(ANY_PATH("swd_scripts"));
+                    FuriString* preselected = furi_string_alloc_printf(
+                        (strlen(ctx->script_detected) > 0) ? ctx->script_detected :
+                                                             ANY_PATH("swd_scripts"));
+                    DialogsFileBrowserOptions options;
+
+                    dialog_file_browser_set_basic_options(&options, "swd", &I_swd);
+
+                    if(dialog_file_browser_show(ctx->dialogs, result_path, preselected, &options)) {
+                        const char* path = furi_string_get_cstr(result_path);
+                        ctx->mode_page = ModePageScript;
+                        swd_execute_script(ctx, path);
+                        ctx->mode_page = mode_page;
+                    }
+
+                    furi_string_free(result_path);
+                    furi_string_free(preselected);
+                    break;
+                } else {
+                    if(ctx->mode_page > 0) {
+                        ctx->mode_page--;
+                    }
+                }
+                break;
+
+            case InputKeyOk:
+                if(ctx->mode_page == ModePageAPID && ctx->apidr_info[ctx->ap_pos].ok) {
+                    ctx->mode_page = ModePageHexDump;
+                } else if(ctx->mode_page == ModePageCoresight) {
+                    uint32_t base = ctx->coresight_bases[ctx->coresight_level];
+
+                    if(!adi_is_romtable(ctx, base)) {
+                        break;
+                    }
+
+                    uint32_t cur_pos = ctx->coresight_pos[ctx->coresight_level];
+                    uint32_t base_next = adi_romtable_get(ctx, base, cur_pos);
+                    uint32_t new_count = adi_romtable_entry_count(ctx, base_next);
+
+                    ctx->coresight_level++;
+                    ctx->coresight_pos[ctx->coresight_level] = 0;
+                    ctx->coresight_count[ctx->coresight_level] = new_count;
+                    ctx->coresight_bases[ctx->coresight_level] = base_next;
+                }
+                break;
+
+            case InputKeyBack:
+                if(ctx->mode_page == ModePageHexDump) {
+                    ctx->mode_page = ModePageAPID;
+                } else if(ctx->mode_page == ModePageScript) {
+                    ctx->script->abort = true;
+                } else if(ctx->mode_page > ModePageFound) {
+                    ctx->mode_page = ModePageScan;
+                } else if(ctx->mode_page == ModePageScan) {
+                    processing = false;
+                } else if(ctx->mode_page == ModePageFound) {
+                    processing = false;
+                }
+                break;
+
+            default:
+                break;
+            }
+        }
+    }
+    return processing;
+}
+
+size_t data_received(void* ctx, uint8_t* data, size_t length) {
+    AppFSM* app = (AppFSM*)ctx;
+
+    strncpy(app->commandline->line_data, (const char*)data, length);
+    app->commandline->line_pos = 0;
+
+    for(size_t pos = 0; pos < length; pos++) {
+        uint8_t ch = app->commandline->line_data[pos];
+
+        if((ch == '\r') || (ch == '\n')) {
+            app->commandline->line_data[pos++] = '\n';
+            app->commandline->line_data[pos] = 0;
+            LOG("direct command '%s'", app->commandline->line_data);
+            swd_execute_script_line(app->commandline);
+            return pos;
+        }
+    }
+
+    return 0;
+}
+
+int32_t swd_probe_app_main(void* p) {
+    UNUSED(p);
+
+    AppFSM* app = malloc(sizeof(AppFSM));
+
+    DBGS("App init");
+    app_init(app);
+
+    DBGS("furi_record_open");
+    app->notification = furi_record_open(RECORD_NOTIFICATION);
+    app->gui = furi_record_open(RECORD_GUI);
+    app->dialogs = furi_record_open(RECORD_DIALOGS);
+    app->storage = furi_record_open(RECORD_STORAGE);
+
+    DBGS("furi_mutex_alloc");
+    app->swd_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+    app->gui_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+    app->event_queue = furi_message_queue_alloc(QUEUE_SIZE, sizeof(AppEvent));
+
+    DBGS("usb_uart_enable");
+    UsbUartConfig uart_config;
+    uart_config.vcp_ch = 1;
+    uart_config.rx_data = &data_received;
+    uart_config.rx_data_ctx = app;
+    app->uart = usb_uart_enable(&uart_config);
+
+    app->commandline = malloc(sizeof(ScriptContext));
+    app->commandline->max_tries = 1;
+    app->commandline->app = app;
+
+    DBGS("view_port_alloc");
+    app->view_port = view_port_alloc();
+    view_port_draw_callback_set(app->view_port, render_callback, app);
+    view_port_input_callback_set(app->view_port, input_callback, app);
+    gui_add_view_port(app->gui, app->view_port, GuiLayerFullscreen);
+
+    DBGS("notification_message_block");
+    notification_message(app->notification, &sequence_display_backlight_enforce_on);
+
+    DBGS("swd_execute_script");
+    swd_execute_script(app, ANY_PATH("swd_scripts/startup.swd"));
+
+    dolphin_deed(DolphinDeedPluginGameStart);
+
+    DBGS("processing");
+    for(bool processing = true; processing;) {
+        swd_main_loop(app);
+        view_port_update(app->view_port);
+
+        processing = swd_message_process(app);
+
+        bool beep = false;
+
+        if(app->detected_device && !app->detected_notified) {
+            app->detected_notified = true;
+            beep = true;
+        }
+        if(!app->detected_device && app->detected_notified) {
+            app->detected_notified = false;
+        }
+        if(beep) {
+            notification_message_block(app->notification, &seq_c_minor);
+        }
+    }
+
+    view_port_enabled_set(app->view_port, false);
+    gui_remove_view_port(app->gui, app->view_port);
+    view_port_free(app->view_port);
+
+    app_deinit(app);
+
+    notification_message(app->notification, &sequence_display_backlight_enforce_auto);
+
+    usb_uart_disable(app->uart);
+
+    furi_message_queue_free(app->event_queue);
+    furi_mutex_free(app->gui_mutex);
+    furi_mutex_free(app->swd_mutex);
+
+    // Reset GPIO pins to default state
+    for(int io = 0; io < 8; io++) {
+        furi_hal_gpio_init(gpios[io], GpioModeAnalog, GpioPullNo, GpioSpeedLow);
+    }
+    free(app);
+
+    furi_record_close(RECORD_GUI);
+    furi_record_close(RECORD_NOTIFICATION);
+    furi_record_close(RECORD_DIALOGS);
+    furi_record_close(RECORD_STORAGE);
+
+    return 0;
+}

+ 244 - 0
swd_probe/swd_probe_app.h

@@ -0,0 +1,244 @@
+#ifndef __SWD_PROBE_APP_H
+#define __SWD_PROBE_APP_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+
+#include <furi.h>
+#include <furi_hal.h>
+#include <furi_hal_speaker.h>
+#include <gui/gui.h>
+#include <gui/elements.h>
+#include <dialogs/dialogs.h>
+#include <input/input.h>
+#include <storage/storage.h>
+#include <dolphin/dolphin.h>
+#include <notification/notification.h>
+#include <notification/notification_messages.h>
+
+#include "usb_uart.h"
+
+#define TAG "SWD"
+
+/* short debug message */
+#define DBGS(format) furi_log_print_format(FuriLogLevelDebug, TAG, "%s: " format, __FUNCTION__)
+/* formatted debug message */
+#define DBG(format, ...) \
+    furi_log_print_format(FuriLogLevelDebug, TAG, "%s: " format, __FUNCTION__, __VA_ARGS__)
+/* log message*/
+#define LOG(...) furi_log_print_format(FuriLogLevelDefault, TAG, __VA_ARGS__)
+
+#define COUNT(x) ((size_t)(sizeof(x) / sizeof((x)[0])))
+#define ARRAY_SIZE(x) COUNT(x)
+
+#define SWD_DELAY_US 0
+#define TIMER_HZ 25
+#define TIMEOUT 3
+#define QUEUE_SIZE 8
+#define IDLE_BITS 8
+#define CLOCK_DELAY 0
+
+#define MAX_FILE_LENGTH 128
+#define SCRIPT_MAX_LINES 1000
+
+typedef enum {
+    ModePageScan = 0,
+    ModePageFound = 1,
+    ModePageDPRegs = 2,
+    ModePageDPID = 3,
+    ModePageAPID = 4,
+    ModePageCount = 5,
+    ModePageHexDump = 0x100,
+    ModePageScript = 0x101,
+    ModePageCoresight = 0x102,
+} ModePages;
+
+#define CDBGPWRUPREQ (1 << 28)
+#define CDBGPWRUPACK (1 << 29)
+#define CSYSPWRUPREQ (1 << 30)
+#define CSYSPWRUPACK (1 << 31)
+#define WDATAERR (1 << 7)
+#define STICKYERR (1 << 5)
+#define STAT_ERROR_FLAGS (WDATAERR | STICKYERR)
+
+#define REG_IDCODE 0x00
+#define REG_CTRLSTAT 0x01
+#define REG_CTRLSTAT_BANK 0x00
+#define REG_DLCR 0x01
+#define REG_DLCR_BANK 0x01
+#define REG_TARGETID 0x01
+#define REG_TARGETID_BANK 0x02
+#define REG_DLPIDR 0x01
+#define REG_DLPIDR_BANK 0x03
+#define REG_EVENTSTAT 0x01
+#define REG_EVENTSTAT_BANK 0x04
+
+#define REG_SELECT 0x02
+
+#define MEMAP_CSW 0x00
+#define MEMAP_TAR 0x04
+#define MEMAP_DRW 0x0C
+#define AP_IDR 0xFC
+#define AP_BASE 0xF8
+
+#define SCS_CPUID 0xE000ED00u
+#define SCS_CPACR 0xE000ED88u
+#define SCS_DHCSR 0xE000EDF0u
+#define SCS_DHCSR_S_HALT (1u << 17)
+#define SCS_DHCSR_C_MASKINTS (1u << 3)
+#define SCS_DHCSR_C_STEP (1u << 2)
+#define SCS_DHCSR_C_HALT (1u << 1)
+#define SCS_DHCSR_C_DEBUGEN (1u << 0)
+#define SCS_DHCSR_KEY 0xA05F0000u
+#define SCS_DCRSR 0xE000EDF4u
+#define SCS_DCRSR_RD 0x00000000u
+#define SCS_DCRSR_WR 0x00010000u
+#define SCS_DCRDR 0xE000EDF8u
+#define SCS_DEMCR 0xE000EDFCu
+
+typedef enum { KeyNone, KeyUp, KeyRight, KeyDown, KeyLeft, KeyOK } KeyCode;
+
+typedef enum {
+    EventTimerTick,
+    EventKeyPress,
+} EventType;
+
+typedef struct {
+    EventType type;
+    InputEvent input;
+} AppEvent;
+
+typedef struct {
+    uint32_t ctrlstat;
+    bool ctrlstat_ok;
+    uint32_t dlcr;
+    bool dlcr_ok;
+    uint32_t dlpidr;
+    bool dlpidr_ok;
+    uint32_t dpidr;
+    bool dpidr_ok;
+    uint32_t eventstat;
+    bool eventstat_ok;
+    uint32_t select;
+    bool select_ok;
+    uint32_t targetid;
+    bool targetid_ok;
+} swd_dpreg_t;
+
+typedef struct {
+    bool ok;
+    bool tested;
+    uint8_t revision;
+    uint16_t designer;
+    uint8_t class;
+    uint8_t variant;
+    uint8_t type;
+    uint32_t base;
+} swd_apidr_info_t;
+
+typedef struct {
+    uint8_t revision;
+    uint8_t partno;
+    uint8_t version;
+    uint16_t designer;
+} swd_dpidr_info_t;
+
+typedef struct {
+    uint8_t revision;
+    uint16_t partno;
+    uint16_t designer;
+} swd_targetid_info_t;
+
+typedef struct sScriptContext ScriptContext;
+
+typedef struct {
+    Storage* storage;
+    Gui* gui;
+    DialogsApp* dialogs;
+    NotificationApp* notification;
+
+    FuriTimer* timer;
+    UsbUart* uart;
+    ViewPort* view_port;
+
+    FuriMessageQueue* event_queue;
+    FuriMutex* swd_mutex;
+    FuriMutex* gui_mutex;
+
+    swd_targetid_info_t targetid_info;
+    swd_dpidr_info_t dpidr_info;
+    swd_dpreg_t dp_regs;
+    swd_apidr_info_t apidr_info[256];
+
+    ScriptContext* script;
+    ScriptContext* commandline;
+
+    uint8_t timeout_overdue;
+    uint32_t loop_count;
+    uint8_t current_mask_id;
+    uint32_t current_mask;
+    uint8_t io_swc;
+    uint8_t io_swd;
+    uint8_t io_num_swc;
+    uint8_t io_num_swd;
+    int32_t detected_timeout;
+    uint32_t swd_clock_delay;
+    uint32_t swd_idle_bits;
+    bool detected;
+    bool detected_device;
+    bool detected_notified;
+    uint32_t mode_page;
+    uint8_t ap_pos;
+    uint8_t ap_scanned;
+
+    uint32_t coresight_pos[16];
+    uint32_t coresight_count[16];
+    uint8_t coresight_level;
+    uint32_t coresight_bases[16];
+
+    uint32_t hex_addr;
+    uint8_t hex_select;
+    uint8_t hex_buffer[32];
+    uint8_t hex_buffer_valid[8];
+
+    char state_string[64];
+    char script_detected[MAX_FILE_LENGTH];
+    bool script_detected_executed;
+} AppFSM;
+
+struct sScriptContext {
+    AppFSM* app;
+    ScriptContext* parent;
+    char filename[MAX_FILE_LENGTH];
+
+    /* when used with string input */
+    char line_data[128];
+    uint64_t line_pos;
+
+    /* when used with file input */
+    File* script_file;
+
+    uint64_t position;
+    uint32_t selected_ap;
+    uint32_t max_tries;
+    uint32_t block_size;
+
+    bool abort;
+    bool restart;
+    bool errors_ignore;
+    bool status_ignore;
+    bool goto_active;
+    char goto_label[64];
+};
+
+typedef struct {
+    const char* prefix;
+    bool (*func)(ScriptContext* ctx);
+} ScriptFunctionInfo;
+
+uint8_t swd_read_memory(AppFSM* const ctx, uint8_t ap, uint32_t address, uint32_t* data);
+
+#endif

+ 229 - 0
swd_probe/usb_uart.c

@@ -0,0 +1,229 @@
+
+#include <stdlib.h>
+
+#include "usb_uart.h"
+#include "furi_hal.h"
+#include <furi_hal_usb_cdc.h>
+#include "usb_cdc.h"
+#include "cli/cli_vcp.h"
+#include <toolbox/api_lock.h>
+#include "cli/cli.h"
+
+#define USB_CDC_PKT_LEN CDC_DATA_SZ
+#define USB_UART_RX_BUF_SIZE (USB_CDC_PKT_LEN * 5)
+
+#define USB_CDC_BIT_DTR (1 << 0)
+#define USB_CDC_BIT_RTS (1 << 1)
+
+typedef enum {
+    WorkerEvtStop = (1 << 0),
+    WorkerEvtCdcRx = (1 << 1),
+    WorkerEvtCfgChange = (1 << 2)
+
+} WorkerEvtFlags;
+
+#define WORKER_ALL_EVENTS (WorkerEvtStop | WorkerEvtCfgChange | WorkerEvtCdcRx)
+
+struct UsbUart {
+    UsbUartConfig cfg;
+    UsbUartConfig cfg_new;
+
+    FuriThread* thread;
+    FuriMutex* usb_mutex;
+    FuriSemaphore* tx_sem;
+    UsbUartState st;
+    FuriApiLock cfg_lock;
+
+    uint8_t rx_buf[USB_CDC_PKT_LEN];
+};
+
+static void vcp_on_cdc_tx_complete(void* context);
+static void vcp_on_cdc_rx(void* context);
+static void vcp_state_callback(void* context, uint8_t state);
+static void vcp_on_cdc_control_line(void* context, uint8_t state);
+static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config);
+
+static const CdcCallbacks cdc_cb = {
+    .tx_ep_callback = &vcp_on_cdc_tx_complete,
+    .rx_ep_callback = &vcp_on_cdc_rx,
+    .state_callback = &vcp_state_callback,
+    .ctrl_line_callback = &vcp_on_cdc_control_line,
+    .config_callback = &vcp_on_line_config};
+
+static void usb_uart_vcp_init(UsbUart* usb_uart, uint8_t vcp_ch) {
+    furi_hal_usb_unlock();
+
+    Cli* cli = furi_record_open(RECORD_CLI);
+    cli_session_close(cli);
+
+    if(vcp_ch == 0) {
+        furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
+    } else {
+        furi_check(furi_hal_usb_set_config(&usb_cdc_dual, NULL) == true);
+        cli_session_open(cli, &cli_vcp);
+    }
+    furi_record_close(RECORD_CLI);
+    furi_hal_cdc_set_callbacks(vcp_ch, (CdcCallbacks*)&cdc_cb, usb_uart);
+}
+
+static void usb_uart_vcp_deinit(UsbUart* usb_uart, uint8_t vcp_ch) {
+    UNUSED(usb_uart);
+    furi_hal_cdc_set_callbacks(vcp_ch, NULL, NULL);
+    if(vcp_ch != 0) {
+        Cli* cli = furi_record_open(RECORD_CLI);
+        cli_session_close(cli);
+        furi_record_close(RECORD_CLI);
+    }
+}
+
+bool usb_uart_tx_data(UsbUart* usb_uart, uint8_t* data, size_t length) {
+    uint32_t pos = 0;
+    while(pos < length) {
+        size_t pkt_size = length - pos;
+
+        if(pkt_size > USB_CDC_PKT_LEN) {
+            pkt_size = USB_CDC_PKT_LEN;
+        }
+
+        if(furi_semaphore_acquire(usb_uart->tx_sem, 100) != FuriStatusOk) {
+            return false;
+        }
+        if(furi_mutex_acquire(usb_uart->usb_mutex, 100) != FuriStatusOk) {
+            furi_semaphore_release(usb_uart->tx_sem);
+            return false;
+        }
+        furi_hal_cdc_send(usb_uart->cfg.vcp_ch, &data[pos], pkt_size);
+        furi_mutex_release(usb_uart->usb_mutex);
+        usb_uart->st.tx_cnt += pkt_size;
+        pos += pkt_size;
+    }
+    return true;
+}
+
+static int32_t usb_uart_worker(void* context) {
+    UsbUart* usb_uart = (UsbUart*)context;
+
+    memcpy(&usb_uart->cfg, &usb_uart->cfg_new, sizeof(UsbUartConfig));
+
+    usb_uart->tx_sem = furi_semaphore_alloc(1, 1);
+    usb_uart->usb_mutex = furi_mutex_alloc(FuriMutexTypeNormal);
+
+    usb_uart_vcp_init(usb_uart, usb_uart->cfg.vcp_ch);
+
+    uint8_t data[2 * USB_CDC_PKT_LEN];
+    size_t remain = 0;
+
+    while(1) {
+        uint32_t events =
+            furi_thread_flags_wait(WORKER_ALL_EVENTS, FuriFlagWaitAny, FuriWaitForever);
+        furi_check(!(events & FuriFlagError));
+
+        if(events & WorkerEvtStop) {
+            break;
+        }
+
+        if(events & WorkerEvtCdcRx) {
+            size_t len = 0;
+            if(furi_mutex_acquire(usb_uart->usb_mutex, 100) == FuriStatusOk) {
+                len = furi_hal_cdc_receive(usb_uart->cfg.vcp_ch, &data[remain], USB_CDC_PKT_LEN);
+                furi_mutex_release(usb_uart->usb_mutex);
+            }
+
+            if(len > 0) {
+                usb_uart->st.rx_cnt += len;
+                remain += len;
+
+                size_t handled = usb_uart->cfg.rx_data(usb_uart->cfg.rx_data_ctx, data, remain);
+
+                memcpy(data, &data[handled], remain - handled);
+                remain -= handled;
+            }
+        }
+
+        if(events & WorkerEvtCfgChange) {
+            if(usb_uart->cfg.vcp_ch != usb_uart->cfg_new.vcp_ch) {
+                usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
+                usb_uart_vcp_init(usb_uart, usb_uart->cfg_new.vcp_ch);
+
+                usb_uart->cfg.vcp_ch = usb_uart->cfg_new.vcp_ch;
+            }
+            api_lock_unlock(usb_uart->cfg_lock);
+        }
+    }
+    usb_uart_vcp_deinit(usb_uart, usb_uart->cfg.vcp_ch);
+
+    furi_mutex_free(usb_uart->usb_mutex);
+    furi_semaphore_free(usb_uart->tx_sem);
+
+    furi_hal_usb_unlock();
+    furi_check(furi_hal_usb_set_config(&usb_cdc_single, NULL) == true);
+    Cli* cli = furi_record_open(RECORD_CLI);
+    cli_session_open(cli, &cli_vcp);
+    furi_record_close(RECORD_CLI);
+
+    return 0;
+}
+
+/* VCP callbacks */
+static void vcp_on_cdc_tx_complete(void* context) {
+    UsbUart* usb_uart = (UsbUart*)context;
+    furi_semaphore_release(usb_uart->tx_sem);
+}
+
+static void vcp_on_cdc_rx(void* context) {
+    UsbUart* usb_uart = (UsbUart*)context;
+    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCdcRx);
+}
+
+static void vcp_state_callback(void* context, uint8_t state) {
+    UNUSED(context);
+    UNUSED(state);
+}
+
+static void vcp_on_cdc_control_line(void* context, uint8_t state) {
+    UNUSED(context);
+    UNUSED(state);
+}
+
+static void vcp_on_line_config(void* context, struct usb_cdc_line_coding* config) {
+    UNUSED(context);
+    UNUSED(config);
+}
+
+UsbUart* usb_uart_enable(UsbUartConfig* cfg) {
+    UsbUart* usb_uart = malloc(sizeof(UsbUart));
+    memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
+
+    usb_uart->thread = furi_thread_alloc_ex("UsbUartWorker", 1024, usb_uart_worker, usb_uart);
+    furi_thread_start(usb_uart->thread);
+    return usb_uart;
+}
+
+void usb_uart_disable(UsbUart* usb_uart) {
+    furi_assert(usb_uart);
+    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtStop);
+    furi_thread_join(usb_uart->thread);
+    furi_thread_free(usb_uart->thread);
+    free(usb_uart);
+}
+
+void usb_uart_set_config(UsbUart* usb_uart, UsbUartConfig* cfg) {
+    furi_assert(usb_uart);
+    furi_assert(cfg);
+    usb_uart->cfg_lock = api_lock_alloc_locked();
+    memcpy(&(usb_uart->cfg_new), cfg, sizeof(UsbUartConfig));
+    furi_thread_flags_set(furi_thread_get_id(usb_uart->thread), WorkerEvtCfgChange);
+    api_lock_wait_unlock_and_free(usb_uart->cfg_lock);
+}
+
+void usb_uart_get_config(UsbUart* usb_uart, UsbUartConfig* cfg) {
+    furi_assert(usb_uart);
+    furi_assert(cfg);
+    memcpy(cfg, &(usb_uart->cfg_new), sizeof(UsbUartConfig));
+}
+
+void usb_uart_get_state(UsbUart* usb_uart, UsbUartState* st) {
+    furi_assert(usb_uart);
+    furi_assert(st);
+    memcpy(st, &(usb_uart->st), sizeof(UsbUartState));
+}

+ 29 - 0
swd_probe/usb_uart.h

@@ -0,0 +1,29 @@
+#pragma once
+
+#include <stdint.h>
+#include <stdbool.h>
+
+typedef struct UsbUart UsbUart;
+
+typedef struct {
+    uint8_t vcp_ch;
+    size_t (*rx_data)(void* ctx, uint8_t* data, size_t length);
+    void* rx_data_ctx;
+} UsbUartConfig;
+
+typedef struct {
+    uint32_t rx_cnt;
+    uint32_t tx_cnt;
+} UsbUartState;
+
+UsbUart* usb_uart_enable(UsbUartConfig* cfg);
+
+void usb_uart_disable(UsbUart* usb_uart);
+
+void usb_uart_set_config(UsbUart* usb_uart, UsbUartConfig* cfg);
+
+void usb_uart_get_config(UsbUart* usb_uart, UsbUartConfig* cfg);
+
+void usb_uart_get_state(UsbUart* usb_uart, UsbUartState* st);
+
+bool usb_uart_tx_data(UsbUart* usb_uart, uint8_t* data, size_t length);