├── .gitignore
├── .gitmodules
├── 3rdparty
└── fc8-compression.c
├── 99-simm-programmer.rules
├── Info.plist
├── LICENSE.txt
├── README.md
├── ROMSIMMFlasher.pro
├── Read Me -- Mac OS X users.txt
├── SIMMProgrammer.icns
├── SIMMProgrammer.ico
├── SIMMProgrammer.rc
├── aboutbox.cpp
├── aboutbox.h
├── aboutbox.ui
├── chipid.cpp
├── chipid.h
├── chipid.qrc
├── chipid.txt
├── createblankdiskdialog.cpp
├── createblankdiskdialog.h
├── createblankdiskdialog.ui
├── droppablegroupbox.cpp
├── droppablegroupbox.h
├── fc8compressor.cpp
├── fc8compressor.h
├── labelwithlinks.cpp
├── labelwithlinks.h
├── main.cpp
├── mainwindow.cpp
├── mainwindow.h
├── mainwindow.ui
├── programmer.cpp
├── programmer.h
├── textbrowserwithlinks.cpp
└── textbrowserwithlinks.h
/.gitignore:
--------------------------------------------------------------------------------
1 | ROMSIMMFlasher.pro.user*
2 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "3rdparty/qextserialport"]
2 | path = 3rdparty/qextserialport
3 | url = https://github.com/dougg3/qextserialport.git
4 | [submodule "3rdparty/fc8-compression"]
5 | path = 3rdparty/fc8-compression
6 | url = https://github.com/steve-chamberlin/fc8-compression.git
7 |
--------------------------------------------------------------------------------
/3rdparty/fc8-compression.c:
--------------------------------------------------------------------------------
1 | // Include the external compression code, but disable some warnings first.
2 |
3 | #pragma GCC diagnostic push
4 | #pragma GCC diagnostic ignored "-Wunused-value"
5 | #pragma GCC diagnostic ignored "-Wsequence-point"
6 | // Clang doesn't have this warning but GCC does...
7 | #if defined(__has_warning)
8 | #if __has_warning("-Wmaybe-uninitialized")
9 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
10 | #endif
11 | #else
12 | #pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
13 | #endif
14 |
15 | #include "3rdparty/fc8-compression/compression.c"
16 |
17 | #pragma GCC diagnostic pop
18 |
--------------------------------------------------------------------------------
/99-simm-programmer.rules:
--------------------------------------------------------------------------------
1 | # udev rule file for Mac ROM SIMM Programmer
2 | ACTION=="add", SUBSYSTEMS=="usb", ATTRS{idVendor}=="16d0", ATTRS{idProduct}=="06aa", MODE="0666", GROUP="plugdev", ENV{ID_MM_DEVICE_IGNORE}="1", ENV{ID_MM_PORT_IGNORE}="1", ENV{ID_MM_TTY_MANUAL_SCAN_ONLY}="1", ENV{ID_MM_TTY_BLACKLIST}="1"
3 |
--------------------------------------------------------------------------------
/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleExecutable
6 | ${EXECUTABLE_NAME}
7 | CFBundleIconFile
8 | ${ASSETCATALOG_COMPILER_APPICON_NAME}
9 | CFBundleIdentifier
10 | ${PRODUCT_BUNDLE_IDENTIFIER}
11 | CFBundlePackageType
12 | APPL
13 | CFBundleSignature
14 | ${QMAKE_PKGINFO_TYPEINFO}
15 | LSMinimumSystemVersion
16 | ${MACOSX_DEPLOYMENT_TARGET}
17 | NOTE
18 | This file was generated by Qt/QMake.
19 | NSPrincipalClass
20 | NSApplication
21 | NSSupportsAutomaticGraphicsSwitching
22 |
23 | CFBundleShortVersionString
24 | ${QMAKE_FULL_VERSION}
25 | CFBundleVersion
26 | ${QMAKE_FULL_VERSION}
27 |
28 |
29 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 2, June 1991
3 |
4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
5 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
6 | Everyone is permitted to copy and distribute verbatim copies
7 | of this license document, but changing it is not allowed.
8 |
9 | Preamble
10 |
11 | The licenses for most software are designed to take away your
12 | freedom to share and change it. By contrast, the GNU General Public
13 | License is intended to guarantee your freedom to share and change free
14 | software--to make sure the software is free for all its users. This
15 | General Public License applies to most of the Free Software
16 | Foundation's software and to any other program whose authors commit to
17 | using it. (Some other Free Software Foundation software is covered by
18 | the GNU Lesser General Public License instead.) You can apply it to
19 | your programs, too.
20 |
21 | When we speak of free software, we are referring to freedom, not
22 | price. Our General Public Licenses are designed to make sure that you
23 | have the freedom to distribute copies of free software (and charge for
24 | this service if you wish), that you receive source code or can get it
25 | if you want it, that you can change the software or use pieces of it
26 | in new free programs; and that you know you can do these things.
27 |
28 | To protect your rights, we need to make restrictions that forbid
29 | anyone to deny you these rights or to ask you to surrender the rights.
30 | These restrictions translate to certain responsibilities for you if you
31 | distribute copies of the software, or if you modify it.
32 |
33 | For example, if you distribute copies of such a program, whether
34 | gratis or for a fee, you must give the recipients all the rights that
35 | you have. You must make sure that they, too, receive or can get the
36 | source code. And you must show them these terms so they know their
37 | rights.
38 |
39 | We protect your rights with two steps: (1) copyright the software, and
40 | (2) offer you this license which gives you legal permission to copy,
41 | distribute and/or modify the software.
42 |
43 | Also, for each author's protection and ours, we want to make certain
44 | that everyone understands that there is no warranty for this free
45 | software. If the software is modified by someone else and passed on, we
46 | want its recipients to know that what they have is not the original, so
47 | that any problems introduced by others will not reflect on the original
48 | authors' reputations.
49 |
50 | Finally, any free program is threatened constantly by software
51 | patents. We wish to avoid the danger that redistributors of a free
52 | program will individually obtain patent licenses, in effect making the
53 | program proprietary. To prevent this, we have made it clear that any
54 | patent must be licensed for everyone's free use or not licensed at all.
55 |
56 | The precise terms and conditions for copying, distribution and
57 | modification follow.
58 |
59 | GNU GENERAL PUBLIC LICENSE
60 | TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
61 |
62 | 0. This License applies to any program or other work which contains
63 | a notice placed by the copyright holder saying it may be distributed
64 | under the terms of this General Public License. The "Program", below,
65 | refers to any such program or work, and a "work based on the Program"
66 | means either the Program or any derivative work under copyright law:
67 | that is to say, a work containing the Program or a portion of it,
68 | either verbatim or with modifications and/or translated into another
69 | language. (Hereinafter, translation is included without limitation in
70 | the term "modification".) Each licensee is addressed as "you".
71 |
72 | Activities other than copying, distribution and modification are not
73 | covered by this License; they are outside its scope. The act of
74 | running the Program is not restricted, and the output from the Program
75 | is covered only if its contents constitute a work based on the
76 | Program (independent of having been made by running the Program).
77 | Whether that is true depends on what the Program does.
78 |
79 | 1. You may copy and distribute verbatim copies of the Program's
80 | source code as you receive it, in any medium, provided that you
81 | conspicuously and appropriately publish on each copy an appropriate
82 | copyright notice and disclaimer of warranty; keep intact all the
83 | notices that refer to this License and to the absence of any warranty;
84 | and give any other recipients of the Program a copy of this License
85 | along with the Program.
86 |
87 | You may charge a fee for the physical act of transferring a copy, and
88 | you may at your option offer warranty protection in exchange for a fee.
89 |
90 | 2. You may modify your copy or copies of the Program or any portion
91 | of it, thus forming a work based on the Program, and copy and
92 | distribute such modifications or work under the terms of Section 1
93 | above, provided that you also meet all of these conditions:
94 |
95 | a) You must cause the modified files to carry prominent notices
96 | stating that you changed the files and the date of any change.
97 |
98 | b) You must cause any work that you distribute or publish, that in
99 | whole or in part contains or is derived from the Program or any
100 | part thereof, to be licensed as a whole at no charge to all third
101 | parties under the terms of this License.
102 |
103 | c) If the modified program normally reads commands interactively
104 | when run, you must cause it, when started running for such
105 | interactive use in the most ordinary way, to print or display an
106 | announcement including an appropriate copyright notice and a
107 | notice that there is no warranty (or else, saying that you provide
108 | a warranty) and that users may redistribute the program under
109 | these conditions, and telling the user how to view a copy of this
110 | License. (Exception: if the Program itself is interactive but
111 | does not normally print such an announcement, your work based on
112 | the Program is not required to print an announcement.)
113 |
114 | These requirements apply to the modified work as a whole. If
115 | identifiable sections of that work are not derived from the Program,
116 | and can be reasonably considered independent and separate works in
117 | themselves, then this License, and its terms, do not apply to those
118 | sections when you distribute them as separate works. But when you
119 | distribute the same sections as part of a whole which is a work based
120 | on the Program, the distribution of the whole must be on the terms of
121 | this License, whose permissions for other licensees extend to the
122 | entire whole, and thus to each and every part regardless of who wrote it.
123 |
124 | Thus, it is not the intent of this section to claim rights or contest
125 | your rights to work written entirely by you; rather, the intent is to
126 | exercise the right to control the distribution of derivative or
127 | collective works based on the Program.
128 |
129 | In addition, mere aggregation of another work not based on the Program
130 | with the Program (or with a work based on the Program) on a volume of
131 | a storage or distribution medium does not bring the other work under
132 | the scope of this License.
133 |
134 | 3. You may copy and distribute the Program (or a work based on it,
135 | under Section 2) in object code or executable form under the terms of
136 | Sections 1 and 2 above provided that you also do one of the following:
137 |
138 | a) Accompany it with the complete corresponding machine-readable
139 | source code, which must be distributed under the terms of Sections
140 | 1 and 2 above on a medium customarily used for software interchange; or,
141 |
142 | b) Accompany it with a written offer, valid for at least three
143 | years, to give any third party, for a charge no more than your
144 | cost of physically performing source distribution, a complete
145 | machine-readable copy of the corresponding source code, to be
146 | distributed under the terms of Sections 1 and 2 above on a medium
147 | customarily used for software interchange; or,
148 |
149 | c) Accompany it with the information you received as to the offer
150 | to distribute corresponding source code. (This alternative is
151 | allowed only for noncommercial distribution and only if you
152 | received the program in object code or executable form with such
153 | an offer, in accord with Subsection b above.)
154 |
155 | The source code for a work means the preferred form of the work for
156 | making modifications to it. For an executable work, complete source
157 | code means all the source code for all modules it contains, plus any
158 | associated interface definition files, plus the scripts used to
159 | control compilation and installation of the executable. However, as a
160 | special exception, the source code distributed need not include
161 | anything that is normally distributed (in either source or binary
162 | form) with the major components (compiler, kernel, and so on) of the
163 | operating system on which the executable runs, unless that component
164 | itself accompanies the executable.
165 |
166 | If distribution of executable or object code is made by offering
167 | access to copy from a designated place, then offering equivalent
168 | access to copy the source code from the same place counts as
169 | distribution of the source code, even though third parties are not
170 | compelled to copy the source along with the object code.
171 |
172 | 4. You may not copy, modify, sublicense, or distribute the Program
173 | except as expressly provided under this License. Any attempt
174 | otherwise to copy, modify, sublicense or distribute the Program is
175 | void, and will automatically terminate your rights under this License.
176 | However, parties who have received copies, or rights, from you under
177 | this License will not have their licenses terminated so long as such
178 | parties remain in full compliance.
179 |
180 | 5. You are not required to accept this License, since you have not
181 | signed it. However, nothing else grants you permission to modify or
182 | distribute the Program or its derivative works. These actions are
183 | prohibited by law if you do not accept this License. Therefore, by
184 | modifying or distributing the Program (or any work based on the
185 | Program), you indicate your acceptance of this License to do so, and
186 | all its terms and conditions for copying, distributing or modifying
187 | the Program or works based on it.
188 |
189 | 6. Each time you redistribute the Program (or any work based on the
190 | Program), the recipient automatically receives a license from the
191 | original licensor to copy, distribute or modify the Program subject to
192 | these terms and conditions. You may not impose any further
193 | restrictions on the recipients' exercise of the rights granted herein.
194 | You are not responsible for enforcing compliance by third parties to
195 | this License.
196 |
197 | 7. If, as a consequence of a court judgment or allegation of patent
198 | infringement or for any other reason (not limited to patent issues),
199 | conditions are imposed on you (whether by court order, agreement or
200 | otherwise) that contradict the conditions of this License, they do not
201 | excuse you from the conditions of this License. If you cannot
202 | distribute so as to satisfy simultaneously your obligations under this
203 | License and any other pertinent obligations, then as a consequence you
204 | may not distribute the Program at all. For example, if a patent
205 | license would not permit royalty-free redistribution of the Program by
206 | all those who receive copies directly or indirectly through you, then
207 | the only way you could satisfy both it and this License would be to
208 | refrain entirely from distribution of the Program.
209 |
210 | If any portion of this section is held invalid or unenforceable under
211 | any particular circumstance, the balance of the section is intended to
212 | apply and the section as a whole is intended to apply in other
213 | circumstances.
214 |
215 | It is not the purpose of this section to induce you to infringe any
216 | patents or other property right claims or to contest validity of any
217 | such claims; this section has the sole purpose of protecting the
218 | integrity of the free software distribution system, which is
219 | implemented by public license practices. Many people have made
220 | generous contributions to the wide range of software distributed
221 | through that system in reliance on consistent application of that
222 | system; it is up to the author/donor to decide if he or she is willing
223 | to distribute software through any other system and a licensee cannot
224 | impose that choice.
225 |
226 | This section is intended to make thoroughly clear what is believed to
227 | be a consequence of the rest of this License.
228 |
229 | 8. If the distribution and/or use of the Program is restricted in
230 | certain countries either by patents or by copyrighted interfaces, the
231 | original copyright holder who places the Program under this License
232 | may add an explicit geographical distribution limitation excluding
233 | those countries, so that distribution is permitted only in or among
234 | countries not thus excluded. In such case, this License incorporates
235 | the limitation as if written in the body of this License.
236 |
237 | 9. The Free Software Foundation may publish revised and/or new versions
238 | of the General Public License from time to time. Such new versions will
239 | be similar in spirit to the present version, but may differ in detail to
240 | address new problems or concerns.
241 |
242 | Each version is given a distinguishing version number. If the Program
243 | specifies a version number of this License which applies to it and "any
244 | later version", you have the option of following the terms and conditions
245 | either of that version or of any later version published by the Free
246 | Software Foundation. If the Program does not specify a version number of
247 | this License, you may choose any version ever published by the Free Software
248 | Foundation.
249 |
250 | 10. If you wish to incorporate parts of the Program into other free
251 | programs whose distribution conditions are different, write to the author
252 | to ask for permission. For software which is copyrighted by the Free
253 | Software Foundation, write to the Free Software Foundation; we sometimes
254 | make exceptions for this. Our decision will be guided by the two goals
255 | of preserving the free status of all derivatives of our free software and
256 | of promoting the sharing and reuse of software generally.
257 |
258 | NO WARRANTY
259 |
260 | 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
261 | FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
262 | OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
263 | PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
264 | OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
265 | MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
266 | TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
267 | PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
268 | REPAIR OR CORRECTION.
269 |
270 | 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
271 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
272 | REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
273 | INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
274 | OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
275 | TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
276 | YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
277 | PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
278 | POSSIBILITY OF SUCH DAMAGES.
279 |
280 | END OF TERMS AND CONDITIONS
281 |
282 | How to Apply These Terms to Your New Programs
283 |
284 | If you develop a new program, and you want it to be of the greatest
285 | possible use to the public, the best way to achieve this is to make it
286 | free software which everyone can redistribute and change under these terms.
287 |
288 | To do so, attach the following notices to the program. It is safest
289 | to attach them to the start of each source file to most effectively
290 | convey the exclusion of warranty; and each file should have at least
291 | the "copyright" line and a pointer to where the full notice is found.
292 |
293 |
294 | Copyright (C)
295 |
296 | This program is free software; you can redistribute it and/or modify
297 | it under the terms of the GNU General Public License as published by
298 | the Free Software Foundation; either version 2 of the License, or
299 | (at your option) any later version.
300 |
301 | This program is distributed in the hope that it will be useful,
302 | but WITHOUT ANY WARRANTY; without even the implied warranty of
303 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
304 | GNU General Public License for more details.
305 |
306 | You should have received a copy of the GNU General Public License along
307 | with this program; if not, write to the Free Software Foundation, Inc.,
308 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
309 |
310 | Also add information on how to contact you by electronic and paper mail.
311 |
312 | If the program is interactive, make it output a short notice like this
313 | when it starts in an interactive mode:
314 |
315 | Gnomovision version 69, Copyright (C) year name of author
316 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
317 | This is free software, and you are welcome to redistribute it
318 | under certain conditions; type `show c' for details.
319 |
320 | The hypothetical commands `show w' and `show c' should show the appropriate
321 | parts of the General Public License. Of course, the commands you use may
322 | be called something other than `show w' and `show c'; they could even be
323 | mouse-clicks or menu items--whatever suits your program.
324 |
325 | You should also get your employer (if you work as a programmer) or your
326 | school, if any, to sign a "copyright disclaimer" for the program, if
327 | necessary. Here is a sample; alter the names:
328 |
329 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program
330 | `Gnomovision' (which makes passes at compilers) written by James Hacker.
331 |
332 | , 1 April 1989
333 | Ty Coon, President of Vice
334 |
335 | This General Public License does not permit incorporating your program into
336 | proprietary programs. If your program is a subroutine library, you may
337 | consider it more useful to permit linking proprietary applications with the
338 | library. If this is what you want to do, use the GNU Lesser General
339 | Public License instead of this License.
340 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Mac ROM SIMM Programmer Software
2 | This is the Windows/Mac/Linux software that goes with the [Mac ROM SIMM Programmer](https://github.com/dougg3/mac-rom-simm-programmer). It allows you to read and write programmable ROM SIMMs, and also update the firmware of the programmer.
3 |
4 | This software is also compatible with the [bigmessowires programmer](http://www.bigmessowires.com/mac-rom-inator-ii-programming/) and the [CayMac Vintage programmer](https://ko-fi.com/s/6f9e9644e4). They are all based on my original design and share the same firmware and software.
5 |
6 | In order for this software to work correctly, make sure you pick the correct size that matches your ROM SIMM in the "SIMM capacity" dropdown at the top of the main window.
7 |
8 | ## Compiling
9 |
10 | This project makes use of the [Qt](https://www.qt.io/) open source widget toolkit. It also uses [qextserialport](https://github.com/qextserialport/qextserialport), which is checked out as a submodule. Note: Older versions of this program required [my special fork of qextserialport](https://github.com/dougg3/doug-qextserialport-linuxnotifications) to be cloned next to this project in order for it to build.
11 |
12 | To check out the project, type the following command:
13 |
14 | `git clone --recursive https://github.com/dougg3/mac-rom-simm-programmer.software.git`
15 |
16 | To compile the project, either open the .pro file in Qt Creator and build it there, or run the following commands inside the directory:
17 |
18 | ```
19 | qmake
20 | make
21 | ```
22 |
23 | This will generate a SIMMProgrammer executable that you can run.
24 |
25 | ## Binaries
26 |
27 | Precompiled binaries are available in the [Releases section](https://github.com/dougg3/mac-rom-simm-programmer.software/releases) of this project.
28 |
29 | ## Linux notes
30 |
31 | There are a few special things to mention about the Linux version of this software. First of all, you need to install libudev-dev as a prerequisite before it will build:
32 |
33 | `sudo apt install libudev-dev`
34 |
35 | By default, the programmer will probably not be accessible to your user account, and ModemManager may attempt to see if it's a modem, which will result in strange behavior like the programmer not operating properly. For example, the programmer software may get stuck waiting forever to communicate with the programmer. The easiest solution is to install a udev rule file for the programmer that sets proper permissions and disables ModemManager from accessing the device. This rule file is called `99-simm-programmer.rules` and it is available in this repository. To install it:
36 |
37 | ```
38 | sudo cp 99-simm-programmer.rules /etc/udev/rules.d/
39 | sudo udevadm control --reload
40 | ```
41 |
42 | After running these commands, unplug and replug your programmer board.
43 |
--------------------------------------------------------------------------------
/ROMSIMMFlasher.pro:
--------------------------------------------------------------------------------
1 | #-------------------------------------------------
2 | #
3 | # Project created by QtCreator 2011-12-14T20:57:35
4 | #
5 | #-------------------------------------------------
6 |
7 | QT += core gui widgets
8 |
9 | TARGET = SIMMProgrammer
10 | TEMPLATE = app
11 | QMAKE_TARGET_BUNDLE_PREFIX = com.downtowndougbrown
12 |
13 | SOURCES += main.cpp\
14 | 3rdparty/fc8-compression.c \
15 | chipid.cpp \
16 | createblankdiskdialog.cpp \
17 | droppablegroupbox.cpp \
18 | fc8compressor.cpp \
19 | labelwithlinks.cpp \
20 | mainwindow.cpp \
21 | programmer.cpp \
22 | aboutbox.cpp \
23 | textbrowserwithlinks.cpp
24 |
25 | HEADERS += mainwindow.h \
26 | 3rdparty/fc8-compression/fc8.h \
27 | chipid.h \
28 | createblankdiskdialog.h \
29 | droppablegroupbox.h \
30 | fc8compressor.h \
31 | labelwithlinks.h \
32 | programmer.h \
33 | aboutbox.h \
34 | textbrowserwithlinks.h
35 |
36 | FORMS += mainwindow.ui \
37 | aboutbox.ui \
38 | createblankdiskdialog.ui
39 |
40 | linux*:CONFIG += qesp_linux_udev
41 | include(3rdparty/qextserialport/src/qextserialport.pri)
42 |
43 | QMAKE_CXXFLAGS_RELEASE += -DQT_NO_DEBUG_OUTPUT
44 |
45 | macx:CONFIG += x86
46 | macx:CONFIG += x86_64
47 |
48 | OTHER_FILES += \
49 | SIMMProgrammer.rc \
50 | Info.plist
51 |
52 | macx:ICON = SIMMProgrammer.icns
53 | lessThan(QT_MAJOR_VERSION, 5) {
54 | # Older Qt required manual resource file for adding icons
55 | win32:RC_FILE = SIMMProgrammer.rc
56 | } else {
57 | # Newer Qt does it automatically, and adds version info too
58 | win32:RC_ICONS = SIMMProgrammer.ico
59 | }
60 |
61 | VERSION = 2.0.1
62 | DEFINES += VERSION_STRING=\\\"$$VERSION\\\"
63 |
64 | macx:QMAKE_INFO_PLIST = Info.plist
65 | win32:QMAKE_TARGET_COMPANY = "Doug Brown"
66 | win32:QMAKE_TARGET_DESCRIPTION = "Mac ROM SIMM Programmer"
67 | win32:QMAKE_TARGET_COPYRIGHT = "Copyright (C) Doug Brown"
68 | win32:QMAKE_TARGET_PRODUCT = "Mac ROM SIMM Programmer"
69 |
70 | DISTFILES += \
71 | chipid.txt
72 |
73 | RESOURCES += \
74 | chipid.qrc
75 |
--------------------------------------------------------------------------------
/Read Me -- Mac OS X users.txt:
--------------------------------------------------------------------------------
1 | -------------------------------------------------------------------------------
2 | Important notice for Mac OS X users:
3 | -------------------------------------------------------------------------------
4 |
5 | When you first plug the programmer board into your computer, Mac OS X will
6 | probably bring up a window telling you that a new network interface has been
7 | detected. This message will perpetually annoy you unless you go into the
8 | Network System Preferences and click "Apply" to add the programmer board as a
9 | network interface.
10 |
11 | The reason it does this is because the programmer board identifies itself to
12 | computers as a USB CDC class modem, which is basically a serial port simulated
13 | over USB that doesn't require drivers in Mac OS X and Linux. The drawback of
14 | this approach is that Mac OS X thinks it is a modem.
15 |
--------------------------------------------------------------------------------
/SIMMProgrammer.icns:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dougg3/mac-rom-simm-programmer.software/b16d25755abdb72664f99c71c6dc4bc37f5b5050/SIMMProgrammer.icns
--------------------------------------------------------------------------------
/SIMMProgrammer.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/dougg3/mac-rom-simm-programmer.software/b16d25755abdb72664f99c71c6dc4bc37f5b5050/SIMMProgrammer.ico
--------------------------------------------------------------------------------
/SIMMProgrammer.rc:
--------------------------------------------------------------------------------
1 | IDI_ICON1 ICON DISCARDABLE "SIMMProgrammer.ico"
2 |
--------------------------------------------------------------------------------
/aboutbox.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2012 Doug Brown
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | *
18 | */
19 |
20 | #include "aboutbox.h"
21 | #include "ui_aboutbox.h"
22 |
23 | AboutBox *AboutBox::instance()
24 | {
25 | // Singleton about box
26 | static AboutBox *_instance = NULL;
27 | if (!_instance)
28 | {
29 | _instance = new AboutBox();
30 | }
31 | return _instance;
32 | }
33 |
34 | AboutBox::AboutBox(QWidget *parent) :
35 | QDialog(parent),
36 | ui(new Ui::AboutBox)
37 | {
38 | ui->setupUi(this);
39 | #if defined(Q_OS_MACX) || defined(Q_OS_LINUX)
40 | resize(width() + 50, height() + 100);
41 | #endif
42 |
43 | ui->versionLabel->setText("Version " VERSION_STRING);
44 | }
45 |
46 | AboutBox::~AboutBox()
47 | {
48 | delete ui;
49 | }
50 |
51 | void AboutBox::on_buttonBox_accepted()
52 | {
53 | this->close();
54 | }
55 |
--------------------------------------------------------------------------------
/aboutbox.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2012 Doug Brown
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | *
18 | */
19 |
20 | #ifndef ABOUTBOX_H
21 | #define ABOUTBOX_H
22 |
23 | #include
24 |
25 | namespace Ui {
26 | class AboutBox;
27 | }
28 |
29 | class AboutBox : public QDialog
30 | {
31 | Q_OBJECT
32 |
33 | public:
34 | static AboutBox *instance();
35 | private slots:
36 | void on_buttonBox_accepted();
37 |
38 | private:
39 | Ui::AboutBox *ui;
40 | explicit AboutBox(QWidget *parent = 0);
41 | ~AboutBox();
42 | };
43 |
44 | #endif // ABOUTBOX_H
45 |
--------------------------------------------------------------------------------
/aboutbox.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | AboutBox
4 |
5 |
6 |
7 | 0
8 | 0
9 | 400
10 | 300
11 |
12 |
13 |
14 | About SIMM Programmer
15 |
16 |
17 | -
18 |
19 |
20 |
21 | 75
22 | true
23 |
24 |
25 |
26 | Mac ROM SIMM Programmer
27 |
28 |
29 | true
30 |
31 |
32 |
33 | -
34 |
35 |
36 | Version 0.0.0
37 |
38 |
39 | true
40 |
41 |
42 |
43 | -
44 |
45 |
46 | <html><head/><body><p>By <a href="http://www.downtowndougbrown.com/"><span style=" text-decoration: underline; color:palette(link);">Doug Brown</span></a>, <a href="http://www.bigmessowires.com"><span style=" text-decoration: underline; color:palette(link);">Steve Chamberlin</span></a></p></body></html>
47 |
48 |
49 | true
50 |
51 |
52 | true
53 |
54 |
55 |
56 | -
57 |
58 |
59 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
60 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
61 | p, li { white-space: pre-wrap; }
62 | </style></head><body style=" font-family:MS Shell Dlg 2,Helvetica,Arial; font-size:11px; font-weight:400; font-style:normal;">
63 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">See the <a href="http://www.bigmessowires.com/mac-rom-inator-ii/"><span style=" text-decoration: underline; color:palette(link);">Mac ROM-inator II page</span></a> for more information, or the <a href="https://68kmla.org/bb/index.php?threads/another-iici-rom-hack.23519/"><span style=" text-decoration: underline; color:palette(link);">68kMLA forums</span></a> for details about this project's origins and its early history.</p></body></html>
64 |
65 |
66 | true
67 |
68 |
69 | true
70 |
71 |
72 |
73 | -
74 |
75 |
76 | Qt::Horizontal
77 |
78 |
79 |
80 | -
81 |
82 |
83 | Credits and licensing:
84 |
85 |
86 |
87 | -
88 |
89 |
90 | <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0//EN" "http://www.w3.org/TR/REC-html40/strict.dtd">
91 | <html><head><meta name="qrichtext" content="1" /><style type="text/css">
92 | p, li { white-space: pre-wrap; }
93 | </style></head><body style=" font-family:MS Shell Dlg 2,Helvetica,Arial; color:palette(text); font-size:11px; font-weight:400; font-style:normal;">
94 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">This program is licensed under the terms of the </span><a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.html"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">GNU General Public License version 2</span></a><span style=" font-size:11px;">. Code is available </span><a href="https://github.com/dougg3/mac-rom-simm-programmer.software"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">here</span></a><span style=" font-size:11px;">.</span></p>
95 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
96 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">It also uses the following libraries:</span></p>
97 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
98 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">• </span><a href="https://github.com/qt/qt5"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">Qt</span></a><span style=" font-size:11px;"> -- for excellent cross-platform user interface compatibility:</span></p>
99 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
100 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">The Qt Toolkit is Copyright (C) 2016 The Qt Company Ltd.</p>
101 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;">Contact: <a href="http://www.qt.io/licensing/"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">http://www.qt.io/licensing/</span></a></p>
102 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
103 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Qt is licensed under the </span><a href="https://www.gnu.org/licenses/old-licenses/gpl-2.0.en.html"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">GNU General Public License version 2</span></a><span style=" font-size:11px;">.</span></p>
104 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
105 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">• </span><a href="https://github.com/qextserialport/qextserialport"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">QextSerialPort</span></a><span style=" font-size:11px;"> -- for communication with the SIMM programmer board:</span></p>
106 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
107 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © 2000-2003 Wayne Roth</span></p>
108 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © 2004-2007 Stefan Sander</span></p>
109 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © 2007 Michal Policht</span></p>
110 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © 2008 Brandon Fosdick</span></p>
111 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © 2009-2010 Liam Staskawicz</span></p>
112 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © 2011 Debao Zhang</span></p>
113 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
114 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">• </span><a href="https://github.com/steve-chamberlin/fc8-compression/"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">FC8 compression</span></a><span style=" font-size:11px;"> by Steve Chamberlin, 2016:</span></p>
115 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
116 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Some concepts and code derived from liblzg by Marcus Geelnard, 2010.</span></p>
117 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
118 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">• The programmer board firmware uses </span><a href="http://www.fourwalledcubicle.com/LUFA.php"><span style=" font-size:11px; text-decoration: underline; color:palette(link);">LUFA</span></a><span style=" font-size:11px;"> for its USB communication:</span></p>
119 | <p style="-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-size:11px;"><br /></p>
120 | <p style=" margin-top:0px; margin-bottom:0px; margin-left:15px; margin-right:0px; -qt-block-indent:0; text-indent:0px;"><span style=" font-size:11px;">Copyright © Dean Camera, 2012</span></p></body></html>
121 |
122 |
123 | true
124 |
125 |
126 |
127 | -
128 |
129 |
130 | Qt::Horizontal
131 |
132 |
133 | QDialogButtonBox::Ok
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 | LabelWithLinks
142 | QLabel
143 |
144 |
145 |
146 | TextBrowserWithLinks
147 | QTextBrowser
148 |
149 |
150 |
151 |
152 |
153 |
154 | buttonBox
155 | accepted()
156 | AboutBox
157 | accept()
158 |
159 |
160 | 248
161 | 254
162 |
163 |
164 | 157
165 | 274
166 |
167 |
168 |
169 |
170 | buttonBox
171 | rejected()
172 | AboutBox
173 | reject()
174 |
175 |
176 | 316
177 | 260
178 |
179 |
180 | 286
181 | 274
182 |
183 |
184 |
185 |
186 |
187 |
--------------------------------------------------------------------------------
/chipid.cpp:
--------------------------------------------------------------------------------
1 | #include "chipid.h"
2 | #include
3 | #include
4 | #include
5 |
6 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
7 | #define MySkipEmptyParts Qt::SkipEmptyParts
8 | #else
9 | #define MySkipEmptyParts QString::SkipEmptyParts
10 | #endif
11 |
12 | ChipID::ChipID(QString filePath, QObject *parent) : QObject(parent)
13 | {
14 | QFile f(filePath);
15 | if (f.open(QFile::ReadOnly))
16 | {
17 | loadChips(f);
18 | f.close();
19 | }
20 |
21 | dummyChipInfo.manufacturer = "Unknown";
22 | dummyChipInfo.manufacturerID = 0;
23 | dummyChipInfo.product = "Unknown";
24 | dummyChipInfo.productID = 0;
25 | dummyChipInfo.width = 0;
26 | dummyChipInfo.capacity = 0;
27 | dummyChipInfo.unlockShifted = false;
28 | }
29 |
30 | bool ChipID::findChips(QList manufacturersStraight, QList devicesStraight, QList manufacturersShifted, QList devicesShifted, QList &info)
31 | {
32 | // Make sure we have a sane amount of info
33 | if (manufacturersStraight.count() != 4 || devicesStraight.count() != 4 ||
34 | manufacturersShifted.count() != 4 || devicesShifted.count() != 4)
35 | {
36 | return false;
37 | }
38 |
39 | QList, QList > > devicesAndManufacturers;
40 | devicesAndManufacturers << qMakePair(manufacturersStraight, devicesStraight);
41 | devicesAndManufacturers << qMakePair(manufacturersShifted, devicesShifted);
42 |
43 | // Try both shift types to see if we can identify the chip type
44 | for (int shiftType = 0; shiftType < 2; shiftType++)
45 | {
46 | const bool shifted = shiftType != 0;
47 | QList const &manufacturers = devicesAndManufacturers[shiftType].first;
48 | QList const &devices = devicesAndManufacturers[shiftType].second;
49 |
50 | ChipInfo const *chipInfo16Bit[2];
51 | ChipInfo const *chipInfo8Bit[4];
52 | uint16_t manufacturers16Bit[2];
53 | uint16_t devices16Bit[2];
54 |
55 | // Look to see if it's a 2-chip SIMM first.
56 | // Combine the ID values from adjacent chips (assuming 4 chips) into 16-bit wide values
57 | for (int i = 0; i < 2; i++)
58 | {
59 | manufacturers16Bit[i] = manufacturers[2*i] | manufacturers[2*i + 1] << 8;
60 | devices16Bit[i] = devices[2*i] | devices[2*i + 1] << 8;
61 | chipInfo16Bit[i] = NULL;
62 | }
63 |
64 | foreach (ChipInfo const &ci, allChips)
65 | {
66 | // Only look for 16-bit chips that match the shift type we're currently looking at
67 | if (ci.width != 16 || ci.unlockShifted != shifted) { continue; }
68 |
69 | for (int i = 0; i < 2; i++)
70 | {
71 | // If we already found a match for this chip, skip it
72 | if (chipInfo16Bit[i]) { continue; }
73 |
74 | if (ci.manufacturerID == manufacturers16Bit[i] &&
75 | ci.productID == devices16Bit[i])
76 | {
77 | chipInfo16Bit[i] = &ci;
78 | }
79 | }
80 | }
81 |
82 | // Now let's try a 4-chip SIMM
83 | for (int i = 0; i < 4; i++)
84 | {
85 | chipInfo8Bit[i] = NULL;
86 | }
87 |
88 | foreach (ChipInfo const &ci, allChips)
89 | {
90 | if (ci.width != 8 || ci.unlockShifted != shifted) { continue; }
91 |
92 | for (int i = 0; i < 4; i++)
93 | {
94 | // If we already found a match for this chip, skip it
95 | if (chipInfo8Bit[i]) { continue; }
96 |
97 | if (ci.manufacturerID == manufacturers[i] &&
98 | ci.productID == devices[i])
99 | {
100 | chipInfo8Bit[i] = &ci;
101 | }
102 | }
103 | }
104 |
105 | // How many of each type were a match?
106 | // If we found any 16-bit matches, assume it's a 2-chip SIMM.
107 | int matches8Bit = 0;
108 | int matches16Bit = 0;
109 | for (int i = 0; i < 2; i++)
110 | {
111 | if (chipInfo16Bit[i]) { matches16Bit++; }
112 | }
113 | for (int i = 0; i < 4; i++)
114 | {
115 | if (chipInfo8Bit[i]) { matches8Bit++; }
116 | }
117 |
118 | if (matches16Bit > 0)
119 | {
120 | // It's a 2-chip SIMM.
121 | for (int i = 0; i < 2; i++)
122 | {
123 | if (chipInfo16Bit[i])
124 | {
125 | info << *chipInfo16Bit[i];
126 | }
127 | else
128 | {
129 | ChipInfo dummy = dummyChipInfo;
130 | dummy.manufacturerID = manufacturers16Bit[i];
131 | dummy.productID = devices16Bit[i];
132 | dummy.unlockShifted = shifted;
133 | info << dummy;
134 | }
135 | }
136 |
137 | return true;
138 | }
139 | else if (matches8Bit > 0)
140 | {
141 | // It's a 4-chip SIMM.
142 | for (int i = 0; i < 4; i++)
143 | {
144 | if (chipInfo8Bit[i])
145 | {
146 | info << *chipInfo8Bit[i];
147 | }
148 | else
149 | {
150 | ChipInfo dummy = dummyChipInfo;
151 | dummy.manufacturerID = manufacturers[i];
152 | dummy.productID = devices[i];
153 | dummy.unlockShifted = shifted;
154 | info << dummy;
155 | }
156 | }
157 |
158 | return true;
159 | }
160 | }
161 |
162 | // If we fall through to here, we didn't find any matches in any method of searching
163 | return false;
164 | }
165 |
166 | void ChipID::loadChips(QIODevice &file)
167 | {
168 | QRegExp whitespace("\\s+");
169 | while (!file.atEnd())
170 | {
171 | QByteArray line = file.readLine().trimmed();
172 | if (line.startsWith(";") || line.isEmpty())
173 | {
174 | continue;
175 | }
176 |
177 | QString lineString = QString::fromUtf8(line);
178 | QStringList components = lineString.split(whitespace, MySkipEmptyParts);
179 | if (components.count() != 8)
180 | {
181 | continue;
182 | }
183 |
184 | ChipInfo info;
185 | info.manufacturer = components[0];
186 | info.product = components[1];
187 | info.width = components[2].toUInt();
188 | info.capacity = components[3].toUInt() * 1024;
189 | QStringList sectorGroups = components[4].split(",");
190 | foreach (QString const §orGroup, sectorGroups)
191 | {
192 | QStringList sectorGroupComponents = sectorGroup.split("*");
193 | QPair numAndSize;
194 | if (sectorGroupComponents.count() == 1)
195 | {
196 | numAndSize.first = 1;
197 | numAndSize.second = decodeSectorSize(sectorGroup);
198 | }
199 | else if (sectorGroupComponents.count() == 2)
200 | {
201 | numAndSize.first = sectorGroupComponents[0].toUInt();
202 | numAndSize.second = decodeSectorSize(sectorGroupComponents[1]);
203 | }
204 | else
205 | {
206 | continue;
207 | }
208 |
209 | if (numAndSize.first != 0 && numAndSize.second != 0)
210 | {
211 | info.sectors << numAndSize;
212 | }
213 | }
214 |
215 | // Sanity-check the sector list against the total capacity
216 | uint32_t sectorTotal = 0;
217 | QPair numAndSize;
218 | foreach (numAndSize, info.sectors)
219 | {
220 | sectorTotal += numAndSize.first * numAndSize.second;
221 | }
222 |
223 | // Account for the fact that in 16-bit mode the sector sizes are in words
224 | if (info.width == 16)
225 | {
226 | sectorTotal *= 2;
227 | }
228 |
229 | if (sectorTotal != info.capacity)
230 | {
231 | qWarning("Chip \"%s %s\" has mismatched sector sizes", qPrintable(info.manufacturer), qPrintable(info.product));
232 | continue;
233 | }
234 |
235 | info.manufacturerID = components[5].toUInt(NULL, 16);
236 | info.productID = components[6].toUInt(NULL, 16);
237 | info.unlockShifted = components[7].toUpper() == "YES";
238 | allChips.append(info);
239 | }
240 | }
241 |
242 | uint32_t ChipID::decodeSectorSize(QString sizeString)
243 | {
244 | uint32_t multiplier = 1;
245 | if (sizeString.endsWith("K"))
246 | {
247 | multiplier = 1024;
248 | sizeString.chop(1);
249 | }
250 | else if (sizeString.endsWith("M"))
251 | {
252 | multiplier = 1048576;
253 | sizeString.chop(1);
254 | }
255 |
256 | bool ok;
257 | uint32_t number = sizeString.toUInt(&ok);
258 | if (ok)
259 | {
260 | return number * multiplier;
261 | }
262 |
263 | return 0;
264 | }
265 |
--------------------------------------------------------------------------------
/chipid.h:
--------------------------------------------------------------------------------
1 | #ifndef CHIPID_H
2 | #define CHIPID_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | class ChipID : public QObject
10 | {
11 | Q_OBJECT
12 |
13 | public:
14 | struct ChipInfo
15 | {
16 | QString manufacturer;
17 | uint16_t manufacturerID;
18 | QString product;
19 | uint16_t productID;
20 | uint8_t width;
21 | uint32_t capacity;
22 | QList > sectors;
23 | bool unlockShifted;
24 | };
25 |
26 | explicit ChipID(QString filePath, QObject *parent = NULL);
27 |
28 | bool findChips(QList manufacturersStraight, QList devicesStraight, QList manufacturersShifted, QList devicesShifted, QList &info);
29 |
30 | private:
31 | void loadChips(QIODevice &file);
32 | static uint32_t decodeSectorSize(QString sizeString);
33 |
34 | ChipInfo dummyChipInfo;
35 | QList allChips;
36 | };
37 |
38 | #endif // CHIPID_H
39 |
--------------------------------------------------------------------------------
/chipid.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | chipid.txt
4 |
5 |
6 |
--------------------------------------------------------------------------------
/chipid.txt:
--------------------------------------------------------------------------------
1 | ; Manufacturer Model Width (Bits) Capacity (KB) Sector Sizes Manufacturer Code Product Code Unlock Shifted
2 | Microchip SST39SF010 8 128 32*4K BF B5 No
3 | Microchip SST39SF020 8 256 64*4K BF B6 No
4 | Microchip SST39SF040 8 512 128*4K BF B7 No
5 | Greenliant GLS29EE010 8 128 1024*128 BF 07 No
6 | Greenliant GLS29SF020 8 256 2048*128 BF 24 No
7 | Greenliant GLS29SF040 8 512 4096*128 BF 13 No
8 | AMD Am29F040B 8 512 8*64K 01 A4 No
9 | Micron M29F200FT 8 256 3*64K,32K,8K,8K,16K 01 51 Yes
10 | Micron M29F200FT 16 256 3*32K,16K,4K,4K,8K 0001 2251 No
11 | Micron M29F200FB 8 256 16K,8K,8K,32K,3*64K 01 57 Yes
12 | Micron M29F200FB 16 256 8K,4K,4K,16K,3*32K 0001 2257 No
13 | Micron M29F400FT 8 512 7*64K,32K,8K,8K,16K 01 23 Yes
14 | Micron M29F400FT 16 512 7*32K,16K,4K,4K,8K 0001 2223 No
15 | Micron M29F400FB 8 512 16K,8K,8K,32K,7*64K 01 AB Yes
16 | Micron M29F400FB 16 512 8K,4K,4K,16K,7*32K 0001 22AB No
17 | Micron M29F800FT 8 1024 15*64K,32K,8K,8K,16K 01 D6 Yes
18 | Micron M29F800FT 16 1024 15*32K,16K,4K,4K,8K 0001 22D6 No
19 | Micron M29F800FB 8 1024 16K,8K,8K,32K,15*64K 01 58 Yes
20 | Micron M29F800FB 16 1024 8K,4K,4K,16K,15*32K 0001 2258 No
21 | Micron M29F160FT 8 2048 31*64K,32K,8K,8K,16K 01 D2 Yes
22 | Micron M29F160FT 16 2048 31*32K,16K,4K,4K,8K 0001 22D2 No
23 | Micron M29F160FB 8 2048 16K,8K,8K,32K,31*64K 01 D8 Yes
24 | Micron M29F160FB 16 2048 8K,4K,4K,16K,31*32K 0001 22D8 No
25 | Macronix MX29F800CT 8 1024 15*64K,32K,8K,8K,16K C2 D6 Yes
26 | Macronix MX29F800CT 16 1024 15*32K,16K,4K,4K,8K 00C2 22D6 No
27 | Macronix MX29F800CB 8 1024 16K,8K,8K,32K,15*64K C2 58 Yes
28 | Macronix MX29F800CB 16 1024 8K,4K,4K,16K,15*32K 00C2 2258 No
29 | ;Macronix MX29LV640ET 8 8192 127*64K,8*8K C2 C9 Yes
30 | ;Macronix MX29LV640ET 16 8192 127*32K,8*4K 00C2 22C9 No
31 | Macronix MX29LV640EB 8 8192 8*8K,127*64K C2 CB Yes
32 | Macronix MX29LV640EB 16 8192 8*4K,127*32K 00C2 22CB No
33 | ;
34 | ; HACKY FIXUPS BELOW:
35 | ;
36 | ; The original "correct" versions of the setup for the MX29LV640ET are commented out above.
37 | ; Below are modified versions for use with the SIMM programmer and Mac ROM SIMMs.
38 | ; We have to assume that the first 2 MB (4-chip) or 4 MB (2-chip) chunk of these chips has the special smaller sectors at the end.
39 | ; This is because we only address a single 8 MB set of address space in the SIMM programmer. SIMMs that use this chip will have a method
40 | ; for addressing any 8 MB chunk of the address space. We have no way of knowing which chunk we're currently addressing.
41 | ; So assume that the tiny sectors are always at the end of the first chunk. If we don't do this and the last chunk is currently being used,
42 | ; we won't erase everything. So this hack is super important for the Garrett's Workshop SIMM, for example.
43 | Macronix MX29LV640ET 8 8192 31*64K,8*8K,95*64K,8*8K C2 C9 Yes
44 | Macronix MX29LV640ET 16 8192 63*32K,8*4K,63*32K,8*4K 00C2 22C9 No
45 |
--------------------------------------------------------------------------------
/createblankdiskdialog.cpp:
--------------------------------------------------------------------------------
1 | #include "createblankdiskdialog.h"
2 | #include "ui_createblankdiskdialog.h"
3 | #include
4 |
5 | CreateBlankDiskDialog::CreateBlankDiskDialog(QWidget *parent) :
6 | QDialog(parent),
7 | ui(new Ui::CreateBlankDiskDialog)
8 | {
9 | ui->setupUi(this);
10 |
11 | // The window needs to be bigger on Mac and Linux due to larger fonts
12 | #if defined(Q_OS_MACX) || defined(Q_OS_LINUX)
13 | resize(width() + 173, height() + 86);
14 | #endif
15 |
16 | ui->buttonBox->addButton("Save...", QDialogButtonBox::AcceptRole);
17 |
18 | bool addedDisabledHeadings = false;
19 |
20 | // Add options for size. First, compressed items...
21 | ui->sizeBox->addItem("Compressed:");
22 | if (!setComboBoxItemEnabled(ui->sizeBox->count() - 1, false))
23 | {
24 | ui->sizeBox->removeItem(ui->sizeBox->count() - 1);
25 | }
26 | else
27 | {
28 | addedDisabledHeadings = true;
29 | }
30 |
31 | ui->sizeBox->addItem("2.35 MB (for 2 MB SIMM, compressed)", 2461696);
32 | ui->sizeBox->addItem("5.5 MB (for 4 MB SIMM, compressed)", 5767168);
33 | ui->sizeBox->addItem("12 MB (for 8 MB SIMM, compressed)", 12582912);
34 |
35 | // Next, uncompressed items...
36 | ui->sizeBox->addItem("Uncompressed:");
37 | if (!setComboBoxItemEnabled(ui->sizeBox->count() - 1, false))
38 | {
39 | ui->sizeBox->removeItem(ui->sizeBox->count() - 1);
40 | }
41 | ui->sizeBox->addItem("1.5 MB (for 2 MB SIMM, uncompressed)", 1572864);
42 | ui->sizeBox->addItem("3.5 MB (for 4 MB SIMM, uncompressed)", 3670016);
43 | ui->sizeBox->addItem("7.5 MB (for 8 MB SIMM, uncompressed)", 7864320);
44 |
45 | // And finally, for Quadra SIMMs:
46 | ui->sizeBox->addItem("Uncompressed, for 1 MB Quadra ROMs:");
47 | if (!setComboBoxItemEnabled(ui->sizeBox->count() - 1, false))
48 | {
49 | ui->sizeBox->removeItem(ui->sizeBox->count() - 1);
50 | }
51 | ui->sizeBox->addItem("1.0 MB (for 2 MB SIMM with Quadra ROM)", 1048576);
52 | ui->sizeBox->addItem("3.0 MB (for 4 MB SIMM with Quadra ROM)", 3145728);
53 | ui->sizeBox->addItem("7.0 MB (for 8 MB SIMM with Quadra ROM)", 7340032);
54 |
55 | // Finally, a section for custom items
56 | ui->sizeBox->addItem("Custom:");
57 | if (!setComboBoxItemEnabled(ui->sizeBox->count() - 1, false))
58 | {
59 | ui->sizeBox->removeItem(ui->sizeBox->count() - 1);
60 | }
61 | ui->sizeBox->addItem("Custom size (specified below)", 0);
62 |
63 | // If we were able to add disabled items, select the second item in the list
64 | // because the first item is a disabled heading
65 | if (addedDisabledHeadings)
66 | {
67 | ui->sizeBox->setCurrentIndex(1);
68 | }
69 |
70 | // Make sure everything is up to date
71 | on_sizeBox_currentIndexChanged(ui->sizeBox->currentIndex());
72 | }
73 |
74 | CreateBlankDiskDialog::~CreateBlankDiskDialog()
75 | {
76 | delete ui;
77 | }
78 |
79 | int CreateBlankDiskDialog::selectedDiskSize() const
80 | {
81 | return ui->customSizeBytesSpinner->value();
82 | }
83 |
84 | bool CreateBlankDiskDialog::setComboBoxItemEnabled(int index, bool enabled)
85 | {
86 | QStandardItemModel *model = qobject_cast(ui->sizeBox->model());
87 | if (!model) { return false; }
88 |
89 | QStandardItem *item = model->item(index);
90 | if (!item) { return false; }
91 |
92 | item->setEnabled(enabled);
93 | return true;
94 | }
95 |
96 | int CreateBlankDiskDialog::truncateToNearest512(int size)
97 | {
98 | // We want the size to be a multiple of 512. Just truncate
99 | // it if we need to.
100 | if (size % 512 != 0)
101 | {
102 | size = (size / 512) * 512;
103 | }
104 | return size;
105 | }
106 |
107 | void CreateBlankDiskDialog::updateFixSectionVisibility()
108 | {
109 | bool badSize = ui->customSizeBytesSpinner->value() % 512 != 0;
110 | ui->fix512BytesSection->setVisible(badSize);
111 | foreach (QAbstractButton *b, ui->buttonBox->buttons())
112 | {
113 | if (b->text() == "Save...")
114 | {
115 | b->setEnabled(!badSize);
116 | }
117 | }
118 | }
119 |
120 | void CreateBlankDiskDialog::on_sizeBox_currentIndexChanged(int index)
121 | {
122 | int size = ui->sizeBox->itemData(index).toInt();
123 |
124 | // If it's custom, enable the size spinners
125 | bool custom = size == 0;
126 | ui->customSizeBytesSpinner->setEnabled(custom);
127 | ui->customSizeMBSpinner->setEnabled(custom);
128 |
129 | // If it's not custom, update the spinners
130 | if (!custom)
131 | {
132 | ui->customSizeBytesSpinner->setValue(size);
133 | // The MB spinner will be updated by this function call
134 | }
135 | }
136 |
137 | void CreateBlankDiskDialog::on_customSizeBytesSpinner_valueChanged(int value)
138 | {
139 | ui->customSizeMBSpinner->blockSignals(true);
140 | ui->customSizeMBSpinner->setValue(static_cast(value) / 1048576.0);
141 | ui->customSizeMBSpinner->blockSignals(false);
142 | updateFixSectionVisibility();
143 | }
144 |
145 | void CreateBlankDiskDialog::on_customSizeMBSpinner_valueChanged(double value)
146 | {
147 | // Truncate it to 512 after converting to bytes
148 | int sizeBytes = truncateToNearest512(qRound(value * 1048576.0));
149 |
150 | // Update the other spinner
151 | ui->customSizeBytesSpinner->blockSignals(true);
152 | ui->customSizeBytesSpinner->setValue(sizeBytes);
153 | ui->customSizeBytesSpinner->blockSignals(false);
154 | }
155 |
156 | void CreateBlankDiskDialog::on_fixButton_clicked()
157 | {
158 | ui->customSizeBytesSpinner->setValue(truncateToNearest512(ui->customSizeBytesSpinner->value()));
159 | updateFixSectionVisibility();
160 | }
161 |
--------------------------------------------------------------------------------
/createblankdiskdialog.h:
--------------------------------------------------------------------------------
1 | #ifndef CREATEBLANKDISKDIALOG_H
2 | #define CREATEBLANKDISKDIALOG_H
3 |
4 | #include
5 |
6 | namespace Ui {
7 | class CreateBlankDiskDialog;
8 | }
9 |
10 | class CreateBlankDiskDialog : public QDialog
11 | {
12 | Q_OBJECT
13 |
14 | public:
15 | explicit CreateBlankDiskDialog(QWidget *parent = NULL);
16 | ~CreateBlankDiskDialog();
17 |
18 | int selectedDiskSize() const;
19 |
20 | private slots:
21 | void on_sizeBox_currentIndexChanged(int index);
22 | void on_customSizeBytesSpinner_valueChanged(int value);
23 | void on_customSizeMBSpinner_valueChanged(double value);
24 | void on_fixButton_clicked();
25 |
26 | private:
27 | bool setComboBoxItemEnabled(int index, bool enabled);
28 | int truncateToNearest512(int size);
29 | void updateFixSectionVisibility();
30 |
31 | Ui::CreateBlankDiskDialog *ui;
32 | };
33 |
34 | #endif // CREATEBLANKDISKDIALOG_H
35 |
--------------------------------------------------------------------------------
/createblankdiskdialog.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | CreateBlankDiskDialog
4 |
5 |
6 |
7 | 0
8 | 0
9 | 358
10 | 274
11 |
12 |
13 |
14 | Create Blank Disk
15 |
16 |
17 | -
18 |
19 |
20 | This screen allows you to create a blank .dsk image. After creating the image, you will need to format it with software such as Basilisk or Mini vMac and copy a system folder to it. Then, you can use it to create a ROM disk of your own.
21 |
22 |
23 | true
24 |
25 |
26 |
27 | -
28 |
29 |
30 | Size:
31 |
32 |
33 |
34 | -
35 |
36 |
37 | -
38 |
39 |
40 | Qt::Horizontal
41 |
42 |
43 | QDialogButtonBox::Cancel
44 |
45 |
46 |
47 | -
48 |
49 |
50 | Qt::Horizontal
51 |
52 |
53 |
54 | 40
55 | 20
56 |
57 |
58 |
59 |
60 | -
61 |
62 |
63 |
-
64 |
65 |
66 |
67 | 0
68 | 0
69 |
70 |
71 |
72 | 104857600
73 |
74 |
75 |
76 | -
77 |
78 |
79 | bytes =
80 |
81 |
82 |
83 | -
84 |
85 |
86 |
87 | 0
88 | 0
89 |
90 |
91 |
92 | 100.000000000000000
93 |
94 |
95 |
96 | -
97 |
98 |
99 | MB
100 |
101 |
102 |
103 |
104 |
105 |
106 | -
107 |
108 |
109 | Qt::Vertical
110 |
111 |
112 |
113 | 20
114 | 40
115 |
116 |
117 |
118 |
119 | -
120 |
121 |
122 |
123 | 0
124 | 0
125 |
126 |
127 |
128 |
-
129 |
130 |
131 | color: red;
132 |
133 |
134 | The size has to be a multiple of 512 bytes.
135 |
136 |
137 |
138 | -
139 |
140 |
141 |
142 | 0
143 | 0
144 |
145 |
146 |
147 | Fix
148 |
149 |
150 |
151 |
152 |
153 |
154 |
155 |
156 |
157 |
158 |
159 | buttonBox
160 | accepted()
161 | CreateBlankDiskDialog
162 | accept()
163 |
164 |
165 | 248
166 | 254
167 |
168 |
169 | 157
170 | 274
171 |
172 |
173 |
174 |
175 | buttonBox
176 | rejected()
177 | CreateBlankDiskDialog
178 | reject()
179 |
180 |
181 | 316
182 | 260
183 |
184 |
185 | 286
186 | 274
187 |
188 |
189 |
190 |
191 |
192 |
--------------------------------------------------------------------------------
/droppablegroupbox.cpp:
--------------------------------------------------------------------------------
1 | #include "droppablegroupbox.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | DroppableGroupBox::DroppableGroupBox(QWidget *parent) :
8 | QGroupBox(parent),
9 | _maxFiles(1)
10 | {
11 | setAcceptDrops(true);
12 | }
13 |
14 | void DroppableGroupBox::dragEnterEvent(QDragEnterEvent *event)
15 | {
16 | if (event->mimeData() && event->mimeData()->hasUrls())
17 | {
18 | QList urls = event->mimeData()->urls();
19 | if (urls.count() > 0 &&
20 | urls.count() <= _maxFiles)
21 | {
22 | foreach (QUrl const &url, urls)
23 | {
24 | if (!url.isLocalFile() ||
25 | !QFile::exists(url.toLocalFile()))
26 | {
27 | return;
28 | }
29 | }
30 | event->accept();
31 | }
32 | }
33 | }
34 |
35 | void DroppableGroupBox::dropEvent(QDropEvent *event)
36 | {
37 | if (event->mimeData() && event->mimeData()->hasUrls())
38 | {
39 | QList urls = event->mimeData()->urls();
40 | if (urls.count() > 0 &&
41 | urls.count() <= _maxFiles)
42 | {
43 | // Make sure we're accepting the drop
44 | foreach (QUrl const &url, urls)
45 | {
46 | if (!url.isLocalFile() ||
47 | !QFile::exists(url.toLocalFile()))
48 | {
49 | return;
50 | }
51 | }
52 |
53 | foreach (QUrl const &url, urls)
54 | {
55 | emit fileDropped(url.toLocalFile());
56 | }
57 |
58 | event->accept();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/droppablegroupbox.h:
--------------------------------------------------------------------------------
1 | #ifndef DROPPABLEGROUPBOX_H
2 | #define DROPPABLEGROUPBOX_H
3 |
4 | #include
5 |
6 | class DroppableGroupBox : public QGroupBox
7 | {
8 | Q_OBJECT
9 | public:
10 | DroppableGroupBox(QWidget *parent = 0);
11 | void setMaxFiles(int maxFiles) { _maxFiles = maxFiles; }
12 |
13 | protected:
14 | void dragEnterEvent(QDragEnterEvent *event);
15 | void dropEvent(QDropEvent *event);
16 |
17 | signals:
18 | void fileDropped(QString path);
19 |
20 | private:
21 | int _maxFiles;
22 | };
23 |
24 | #endif // DROPPABLEGROUPBOX_H
25 |
--------------------------------------------------------------------------------
/fc8compressor.cpp:
--------------------------------------------------------------------------------
1 | #include "fc8compressor.h"
2 | #include
3 | #include
4 | namespace fc8 {
5 | extern "C" {
6 | #include "3rdparty/fc8-compression/fc8.h"
7 | }
8 | }
9 |
10 | static QCryptographicHash::Algorithm hashAlgorithm()
11 | {
12 | // Preserve Qt 4 compatibility, just in case...
13 | #if (QT_VERSION >= QT_VERSION_CHECK(5, 0, 0))
14 | return QCryptographicHash::Sha256;
15 | #else
16 | return QCryptographicHash::Sha1;
17 | #endif
18 | }
19 |
20 | FC8Compressor::FC8Compressor(const QByteArray &data, int blockSize, QObject *parent) :
21 | QObject(parent),
22 | _data(data),
23 | _blockSize(blockSize)
24 | {
25 |
26 | }
27 |
28 | void FC8Compressor::doCompression()
29 | {
30 | QByteArray compressedData(2 * _data.length(), static_cast(0));
31 | if (_blockSize == 0)
32 | {
33 | uint32_t len = fc8::Encode(reinterpret_cast(_data.constData()), _data.length(),
34 | reinterpret_cast(compressedData.data()), compressedData.length());
35 | // the encode routine returns the compressed length, or 0 if there's an error
36 | compressedData.truncate(len);
37 | }
38 | else
39 | {
40 | int numBlocks = (_data.length() - 1) / _blockSize + 1;
41 | // If the input data isn't a multiple of the block size, make sure we've
42 | // reserved enough room in the compression buffer
43 | if (numBlocks * _blockSize > _data.length())
44 | {
45 | const int extraBytes = numBlocks * _blockSize - _data.length();
46 | compressedData.append(QByteArray(2 * extraBytes, static_cast(0)));
47 | }
48 |
49 | // Fill out the header
50 | compressedData.replace(0, 4, "FC8b", 4);
51 | compressedData[FC8_DECODED_SIZE_OFFSET + 0] = (_data.length() >> 24) & 0xFF;
52 | compressedData[FC8_DECODED_SIZE_OFFSET + 1] = (_data.length() >> 16) & 0xFF;
53 | compressedData[FC8_DECODED_SIZE_OFFSET + 2] = (_data.length() >> 8) & 0xFF;
54 | compressedData[FC8_DECODED_SIZE_OFFSET + 3] = (_data.length() >> 0) & 0xFF;
55 | compressedData[FC8_BLOCK_SIZE_OFFSET + 0] = (_blockSize >> 24) & 0xFF;
56 | compressedData[FC8_BLOCK_SIZE_OFFSET + 1] = (_blockSize >> 16) & 0xFF;
57 | compressedData[FC8_BLOCK_SIZE_OFFSET + 2] = (_blockSize >> 8) & 0xFF;
58 | compressedData[FC8_BLOCK_SIZE_OFFSET + 3] = (_blockSize >> 0) & 0xFF;
59 |
60 | int blockpos = FC8_BLOCK_HEADER_SIZE;
61 | int pos = FC8_BLOCK_HEADER_SIZE + (4 * numBlocks);
62 | for (int i = 0; i < numBlocks; i++)
63 | {
64 | // Grab another block to write out. Pad it with zeros to the block size if
65 | // it's the last block and the input data wasn't a multiple of the block size.
66 | int chunkLen = qMin(_blockSize, _data.length() - (i * _blockSize));
67 | QByteArray block = QByteArray::fromRawData(_data.constData() + i * _blockSize, chunkLen);
68 | if (chunkLen < _blockSize)
69 | {
70 | block.append(QByteArray(_blockSize - chunkLen, static_cast(0)));
71 | }
72 |
73 | // Compress this block
74 | uint32_t len = fc8::Encode(reinterpret_cast(block.constData()), _blockSize,
75 | reinterpret_cast(compressedData.data() + pos), compressedData.length() - pos);
76 | if (len == 0)
77 | {
78 | // Error occurred during encoding. Signal with an empty QByteArray to signal an error
79 | compressedData.clear();
80 | break;
81 | }
82 |
83 | // Save the start location of this block in the block table
84 | compressedData[blockpos + 0] = (pos >> 24) & 0xFF;
85 | compressedData[blockpos + 1] = (pos >> 16) & 0xFF;
86 | compressedData[blockpos + 2] = (pos >> 8) & 0xFF;
87 | compressedData[blockpos + 3] = (pos >> 0) & 0xFF;
88 |
89 | // Move forward
90 | blockpos += 4;
91 | pos += len;
92 | }
93 |
94 | // All done; now truncate the compressed array to where we left off
95 | compressedData.truncate(pos);
96 | }
97 |
98 | // Calculate a signature of the original file so we can associate the compressed version
99 | // with the original.
100 | QByteArray hashOfOriginal = QCryptographicHash::hash(_data, hashAlgorithm());
101 | emit compressionFinished(hashOfOriginal, compressedData);
102 | }
103 |
104 | bool FC8Compressor::hashMatchesFile(const QByteArray &hash, const QByteArray &file)
105 | {
106 | return QCryptographicHash::hash(file, hashAlgorithm()) == hash;
107 | }
108 |
--------------------------------------------------------------------------------
/fc8compressor.h:
--------------------------------------------------------------------------------
1 | #ifndef FC8COMPRESSOR_H
2 | #define FC8COMPRESSOR_H
3 |
4 | #include
5 | #include
6 |
7 | class FC8Compressor : public QObject
8 | {
9 | Q_OBJECT
10 | public:
11 | explicit FC8Compressor(QByteArray const &data, int blockSize, QObject *parent = NULL);
12 |
13 | public slots:
14 | void doCompression();
15 | static bool hashMatchesFile(QByteArray const &hash, QByteArray const &file);
16 |
17 | signals:
18 | void compressionFinished(QByteArray hashOfOriginal, QByteArray compressedData);
19 |
20 | private:
21 | QByteArray _data;
22 | int _blockSize;
23 | };
24 |
25 | #endif // FC8COMPRESSOR_H
26 |
--------------------------------------------------------------------------------
/labelwithlinks.cpp:
--------------------------------------------------------------------------------
1 | #include "labelwithlinks.h"
2 | #include
3 |
4 | LabelWithLinks::LabelWithLinks(QWidget *parent) :
5 | QLabel(parent)
6 | {
7 |
8 | }
9 |
10 | void LabelWithLinks::changeEvent(QEvent *event)
11 | {
12 | if (event->type() == QEvent::PaletteChange)
13 | {
14 | QString tmp = text();
15 | setText("");
16 | setText(tmp);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/labelwithlinks.h:
--------------------------------------------------------------------------------
1 | #ifndef LABELWITHLINKS_H
2 | #define LABELWITHLINKS_H
3 |
4 | #include
5 |
6 | class LabelWithLinks : public QLabel
7 | {
8 | Q_OBJECT
9 | public:
10 | explicit LabelWithLinks(QWidget *parent = NULL);
11 |
12 | protected:
13 | virtual void changeEvent(QEvent *event);
14 | };
15 |
16 | #endif // LABELWITHLINKS_H
17 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2012 Doug Brown
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | *
18 | */
19 |
20 | #include
21 | #include "mainwindow.h"
22 |
23 | int main(int argc, char *argv[])
24 | {
25 | QApplication a(argc, argv);
26 | MainWindow w;
27 | w.show();
28 |
29 | return a.exec();
30 | }
31 |
--------------------------------------------------------------------------------
/mainwindow.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2012 Doug Brown
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | *
18 | */
19 |
20 | #ifndef MAINWINDOW_H
21 | #define MAINWINDOW_H
22 |
23 | #include
24 | #include
25 | #include
26 | #include "programmer.h"
27 |
28 | namespace Ui {
29 | class MainWindow;
30 | }
31 |
32 | class MainWindow : public QMainWindow
33 | {
34 | Q_OBJECT
35 |
36 | public:
37 | explicit MainWindow(QWidget *parent = 0);
38 | ~MainWindow();
39 |
40 | private slots:
41 | void on_selectWriteFileButton_clicked();
42 | void on_selectReadFileButton_clicked();
43 |
44 | void on_writeToSIMMButton_clicked();
45 | void doInternalWrite(QIODevice *device);
46 | void on_readFromSIMMButton_clicked();
47 |
48 | void on_chosenWriteFile_textEdited(const QString &newText);
49 | void on_chosenReadFile_textEdited(const QString &newText);
50 |
51 | void on_writeGroupBox_fileDropped(const QString &filePath);
52 | void on_readGroupBox_fileDropped(const QString &filePath);
53 | void on_createROMGroupBox_fileDropped(const QString &filePath);
54 |
55 | void programmerWriteStatusChanged(WriteStatus newStatus);
56 | void programmerWriteTotalLengthChanged(uint32_t totalLen);
57 | void programmerWriteCompletionLengthChanged(uint32_t len);
58 |
59 | void programmerVerifyTotalLengthChanged(uint32_t totalLen);
60 | void programmerVerifyCompletionLengthChanged(uint32_t len);
61 |
62 | void programmerElectricalTestStatusChanged(ElectricalTestStatus newStatus);
63 | void programmerElectricalTestLocation(uint8_t loc1, uint8_t loc2);
64 |
65 | void programmerReadStatusChanged(ReadStatus newStatus);
66 | void programmerReadTotalLengthChanged(uint32_t totalLen);
67 | void programmerReadCompletionLengthChanged(uint32_t len);
68 |
69 | void programmerIdentifyStatusChanged(IdentificationStatus newStatus);
70 |
71 | void programmerFirmwareFlashStatusChanged(FirmwareFlashStatus newStatus);
72 | void programmerFirmwareFlashTotalLengthChanged(uint32_t totalLen);
73 | void programmerFirmwareFlashCompletionLengthChanged(uint32_t len);
74 |
75 | void programmerFirmwareVersionStatusChanged(ReadFirmwareVersionStatus status, uint32_t version);
76 |
77 | void on_electricalTestButton_clicked();
78 |
79 | void on_actionUpdate_firmware_triggered();
80 |
81 | void on_identifyButton_clicked();
82 |
83 | // Handlers for when the programmer board has been connected or disconnected
84 | void programmerBoardConnected();
85 | void programmerBoardDisconnected();
86 | void programmerBoardDisconnectedDuringOperation();
87 |
88 | void on_simmCapacityBox_currentIndexChanged(int index);
89 |
90 | void on_actionAbout_SIMM_Programmer_triggered();
91 | void on_actionCheck_Firmware_Version_triggered();
92 |
93 | void on_verifyBox_currentIndexChanged(int index);
94 | void on_createVerifyBox_currentIndexChanged(int index);
95 |
96 | void on_howMuchToWriteBox_currentIndexChanged(int index);
97 | void on_createHowMuchToWriteBox_currentIndexChanged(int index);
98 |
99 | void on_flashIndividualEnterButton_clicked();
100 | void on_returnNormalButton_clicked();
101 |
102 | void updateFlashIndividualControlsEnabled();
103 | void selectIndividualWriteFileClicked();
104 | void selectIndividualReadFileClicked();
105 |
106 | void on_multiFlashChipsButton_clicked();
107 | void on_multiReadChipsButton_clicked();
108 | void finishMultiRead();
109 |
110 | void on_verifyROMChecksumButton_clicked();
111 | void finishChecksumVerify();
112 | bool calculateROMChecksum(QByteArray const &rom, uint32_t len, uint32_t &checksum);
113 |
114 | void on_selectBaseROMButton_clicked();
115 | void on_selectDiskImageButton_clicked();
116 | void on_chosenBaseROMFile_textEdited(const QString &text);
117 | void on_chosenDiskImageFile_textEdited(const QString &text);
118 | void updateCreateROMControlStatus();
119 | void on_writeCombinedFileToSIMMButton_clicked();
120 | void on_saveCombinedFileButton_clicked();
121 |
122 | void compressorThreadFinished(QByteArray hashOfOriginal, QByteArray compressedData);
123 |
124 | void messageBoxFinished();
125 |
126 | void on_actionExtended_UI_triggered(bool checked);
127 |
128 | void on_actionCreate_blank_disk_image_triggered();
129 |
130 | private:
131 | Ui::MainWindow *ui;
132 | bool initializing;
133 | QIODevice *writeFile;
134 | QFile *readFile;
135 | QString electricalTestString;
136 | QBuffer *writeBuffer;
137 | QBuffer *readBuffer;
138 | QBuffer *checksumVerifyBuffer;
139 | QByteArray compressedImageFileHash;
140 | QByteArray compressedImage;
141 | QMessageBox *activeMessageBox;
142 |
143 | enum KnownBaseROM
144 | {
145 | BaseROMUnknown,
146 | BaseROMbbraun2MB,
147 | BaseROMbbraun8MB,
148 | BaseROMBMOW,
149 | BaseROMGarrettsWorkshop,
150 | BaseROMbbraunInQuadra,
151 | };
152 |
153 | void resetAndShowStatusPage();
154 | void handleVerifyFailureReply();
155 |
156 | void hideFlashIndividualControls();
157 | void showFlashIndividualControls();
158 |
159 | void returnToControlPage();
160 |
161 | bool checkBaseROMValidity(QString &errorText);
162 | bool checkBaseROMCompressionSupport();
163 | KnownBaseROM identifyBaseROM(QByteArray const *baseROMToCheck = NULL);
164 | int offsetToQuadraROMDiskSize(QByteArray const &baseROM);
165 | bool checkDiskImageValidity(QString &errorText, bool &alreadyCompressed);
166 | bool isCompressedDiskImage(QByteArray const &image);
167 | void compressImageInBackground(QByteArray uncompressedImage, bool blockUntilCompletion);
168 | QByteArray uncompressedDiskImage();
169 | QByteArray diskImageToWrite();
170 | QByteArray unpatchedBaseROM();
171 | QByteArray patchedBaseROM();
172 | QByteArray createROM();
173 | QString displayableFileSize(qint64 size);
174 |
175 | static QList separateFirmwareIntoVersions(QByteArray totalFirmware);
176 | QByteArray findCompatibleFirmware(QString filename, QString &compatibilityError);
177 | bool firmwareIsCompatible(QByteArray const &firmware, bool &isFirmwareFile);
178 |
179 | void showMessageBox(QMessageBox::Icon icon, const QString &title, const QString &text);
180 | void setUseExtendedUI(bool extended);
181 | };
182 |
183 | #endif // MAINWINDOW_H
184 |
--------------------------------------------------------------------------------
/mainwindow.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | MainWindow
4 |
5 |
6 |
7 | 0
8 | 0
9 | 500
10 | 280
11 |
12 |
13 |
14 | SIMM Programmer
15 |
16 |
17 | QTabWidget QGroupBox#writeGroupBox, QTabWidget QGroupBox#readGroupBox, QTabWidget QGroupBox#createROMGroupBox {
18 | border: 0px solid;
19 | }
20 |
21 |
22 |
23 |
24 | 0
25 |
26 |
27 | 0
28 |
29 |
30 | 0
31 |
32 |
33 | 0
34 |
35 |
36 | 0
37 |
38 | -
39 |
40 |
41 | 0
42 |
43 |
44 |
45 |
-
46 |
47 |
-
48 |
49 |
50 | Qt::Horizontal
51 |
52 |
53 |
54 | 40
55 | 20
56 |
57 |
58 |
59 |
60 | -
61 |
62 |
63 | SIMM capacity:
64 |
65 |
66 |
67 | -
68 |
69 |
70 |
71 |
72 | -
73 |
74 |
75 | 0
76 |
77 |
78 |
79 | Write file to SIMM
80 |
81 |
82 |
83 | 0
84 |
85 |
86 | 0
87 |
88 |
89 | 0
90 |
91 |
92 | 0
93 |
94 |
95 | 0
96 |
97 |
-
98 |
99 |
100 |
101 |
102 |
103 |
-
104 |
105 |
-
106 |
107 |
108 | false
109 |
110 |
111 |
112 | -
113 |
114 |
115 | Select file...
116 |
117 |
118 |
119 |
120 |
121 | -
122 |
123 |
-
124 |
125 |
126 |
127 | 0
128 | 0
129 |
130 |
131 |
132 |
133 | -
134 |
135 |
136 |
137 | 0
138 | 0
139 |
140 |
141 |
142 |
143 | -
144 |
145 |
146 | Qt::Horizontal
147 |
148 |
149 |
150 | 40
151 | 20
152 |
153 |
154 |
155 |
156 | -
157 |
158 |
159 | Write to SIMM
160 |
161 |
162 |
163 |
164 |
165 | -
166 |
167 |
168 | Qt::Vertical
169 |
170 |
171 |
172 | 20
173 | 40
174 |
175 |
176 |
177 |
178 |
179 |
180 |
181 |
182 |
183 |
184 |
185 | Create ROM with ROM disk
186 |
187 |
188 |
189 | 0
190 |
191 |
192 | 0
193 |
194 |
195 | 0
196 |
197 |
198 | 0
199 |
200 |
201 | 0
202 |
203 | -
204 |
205 |
206 |
-
207 |
208 |
-
209 |
210 |
211 | Select base ROM...
212 |
213 |
214 |
215 | -
216 |
217 |
218 | false
219 |
220 |
221 |
222 | -
223 |
224 |
225 | false
226 |
227 |
228 |
229 | -
230 |
231 |
232 | Select disk image...
233 |
234 |
235 |
236 |
237 |
238 | -
239 |
240 |
-
241 |
242 |
243 |
244 | 0
245 | 0
246 |
247 |
248 |
249 |
250 | -
251 |
252 |
253 |
254 | 0
255 | 0
256 |
257 |
258 |
259 |
260 | -
261 |
262 |
263 | Qt::Horizontal
264 |
265 |
266 |
267 | 40
268 | 20
269 |
270 |
271 |
272 |
273 | -
274 |
275 |
276 | Save to file...
277 |
278 |
279 |
280 | -
281 |
282 |
283 | Write to SIMM
284 |
285 |
286 |
287 |
288 |
289 | -
290 |
291 |
292 | color: red;
293 |
294 |
295 | TextLabel
296 |
297 |
298 |
299 | -
300 |
301 |
302 | Qt::Vertical
303 |
304 |
305 |
306 | 20
307 | 40
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 | Read from SIMM to file
320 |
321 |
322 |
323 | 0
324 |
325 |
326 | 0
327 |
328 |
329 | 0
330 |
331 |
332 | 0
333 |
334 |
335 | 0
336 |
337 | -
338 |
339 |
340 |
341 |
342 |
343 |
-
344 |
345 |
-
346 |
347 |
348 | false
349 |
350 |
351 |
352 | -
353 |
354 |
355 | Select file...
356 |
357 |
358 |
359 |
360 |
361 | -
362 |
363 |
364 | 0
365 |
366 |
-
367 |
368 |
369 | Qt::Horizontal
370 |
371 |
372 |
373 | 40
374 | 20
375 |
376 |
377 |
378 |
379 | -
380 |
381 |
382 | Verify ROM Checksum...
383 |
384 |
385 |
386 | -
387 |
388 |
389 | Read from SIMM
390 |
391 |
392 |
393 |
394 |
395 | -
396 |
397 |
398 | Qt::Vertical
399 |
400 |
401 |
402 | 20
403 | 40
404 |
405 |
406 |
407 |
408 |
409 |
410 |
411 |
412 |
413 |
414 |
415 | -
416 |
417 |
418 | Miscellaneous
419 |
420 |
421 |
-
422 |
423 |
-
424 |
425 |
426 | Flash individual chips...
427 |
428 |
429 |
430 | -
431 |
432 |
433 | Qt::Horizontal
434 |
435 |
436 |
437 | 40
438 | 20
439 |
440 |
441 |
442 |
443 | -
444 |
445 |
446 | Identify chips
447 |
448 |
449 |
450 | -
451 |
452 |
453 | Electrical test
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 | -
463 |
464 |
465 | Qt::Vertical
466 |
467 |
468 |
469 | 20
470 | 40
471 |
472 |
473 |
474 |
475 |
476 |
477 |
478 |
479 | -
480 |
481 |
482 |
483 | 0
484 | 0
485 |
486 |
487 |
488 | Status label:
489 |
490 |
491 | Qt::AlignCenter
492 |
493 |
494 |
495 | -
496 |
497 |
498 | 0
499 |
500 |
-
501 |
502 |
503 | 24
504 |
505 |
506 |
507 | -
508 |
509 |
510 | Cancel
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 | -
521 |
522 |
523 | Please connect the SIMM programmer board to your computer in order to use this software.
524 |
525 |
526 | Qt::AlignCenter
527 |
528 |
529 |
530 |
531 |
532 |
533 |
534 | -
535 |
536 |
537 | Flash file(s) to chips
538 |
539 |
540 |
-
541 |
542 |
543 | QFormLayout::AllNonFixedFieldsGrow
544 |
545 |
-
546 |
547 |
548 | Flash IC1:
549 |
550 |
551 |
552 | -
553 |
554 |
-
555 |
556 |
557 | -
558 |
559 |
560 | Select file...
561 |
562 |
563 |
564 |
565 |
566 | -
567 |
568 |
569 | Flash IC2:
570 |
571 |
572 |
573 | -
574 |
575 |
-
576 |
577 |
578 | -
579 |
580 |
581 | Select file...
582 |
583 |
584 |
585 |
586 |
587 | -
588 |
589 |
-
590 |
591 |
592 | -
593 |
594 |
595 | Select file...
596 |
597 |
598 |
599 |
600 |
601 | -
602 |
603 |
-
604 |
605 |
606 | -
607 |
608 |
609 | Select file...
610 |
611 |
612 |
613 |
614 |
615 | -
616 |
617 |
618 | Flash IC3:
619 |
620 |
621 |
622 | -
623 |
624 |
625 | Flash IC4:
626 |
627 |
628 |
629 |
630 |
631 | -
632 |
633 |
-
634 |
635 |
636 | Qt::Horizontal
637 |
638 |
639 |
640 | 40
641 | 20
642 |
643 |
644 |
645 |
646 | -
647 |
648 |
649 | Flash chip(s)
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 | -
659 |
660 |
661 | Read files(s) from chips
662 |
663 |
664 |
-
665 |
666 |
667 | QFormLayout::AllNonFixedFieldsGrow
668 |
669 |
-
670 |
671 |
672 | Read IC1:
673 |
674 |
675 |
676 | -
677 |
678 |
-
679 |
680 |
681 | -
682 |
683 |
684 | Select file...
685 |
686 |
687 |
688 |
689 |
690 | -
691 |
692 |
693 | Read IC2:
694 |
695 |
696 |
697 | -
698 |
699 |
-
700 |
701 |
702 | -
703 |
704 |
705 | Select file...
706 |
707 |
708 |
709 |
710 |
711 | -
712 |
713 |
-
714 |
715 |
716 | -
717 |
718 |
719 | Select file...
720 |
721 |
722 |
723 |
724 |
725 | -
726 |
727 |
-
728 |
729 |
730 | -
731 |
732 |
733 | Select file...
734 |
735 |
736 |
737 |
738 |
739 | -
740 |
741 |
742 | Read IC3:
743 |
744 |
745 |
746 | -
747 |
748 |
749 | Read IC4:
750 |
751 |
752 |
753 |
754 |
755 | -
756 |
757 |
-
758 |
759 |
760 | Qt::Horizontal
761 |
762 |
763 |
764 | 40
765 | 20
766 |
767 |
768 |
769 |
770 | -
771 |
772 |
773 | Read chip(s)
774 |
775 |
776 |
777 |
778 |
779 |
780 |
781 |
782 | -
783 |
784 |
-
785 |
786 |
787 | This mode allows you to flash/read individual chips on a SIMM
788 |
789 |
790 |
791 | -
792 |
793 |
794 | Qt::Horizontal
795 |
796 |
797 |
798 | 40
799 | 20
800 |
801 |
802 |
803 |
804 | -
805 |
806 |
807 | Return to normal mode
808 |
809 |
810 |
811 |
812 |
813 |
814 |
815 |
816 |
817 |
818 |
819 |
854 |
855 |
856 | Quit
857 |
858 |
859 | Ctrl+Q
860 |
861 |
862 |
863 |
864 | Update firmware...
865 |
866 |
867 |
868 |
869 | About SIMM Programmer...
870 |
871 |
872 | QAction::AboutRole
873 |
874 |
875 |
876 |
877 | true
878 |
879 |
880 | Extended View
881 |
882 |
883 |
884 |
885 | Create blank disk image...
886 |
887 |
888 |
889 |
890 | Check firmware version...
891 |
892 |
893 |
894 |
895 |
896 |
897 | DroppableGroupBox
898 | QGroupBox
899 |
900 | 1
901 |
902 |
903 |
904 |
905 |
906 | actionQuit
907 | triggered()
908 | MainWindow
909 | close()
910 |
911 |
912 | -1
913 | -1
914 |
915 |
916 | 278
917 | 201
918 |
919 |
920 |
921 |
922 |
923 |
--------------------------------------------------------------------------------
/programmer.cpp:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2012 Doug Brown
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | *
18 | */
19 |
20 | #include "programmer.h"
21 | #include
22 | #include
23 | #include
24 | #include
25 |
26 | typedef enum ProgrammerCommandState
27 | {
28 | WaitingForNextCommand = 0,
29 |
30 | WriteSIMMWaitingSetSectorLayoutReply,
31 | WriteSIMMWaitingSectorLayoutDataReply,
32 | WriteSIMMWaitingSetSizeReply,
33 | WriteSIMMWaitingSetVerifyModeReply,
34 | WriteSIMMWaitingSetChipMaskReply,
35 | WriteSIMMWaitingSetChipMaskValueReply,
36 | WriteSIMMWaitingEraseReply,
37 | WriteSIMMWaitingWriteReply,
38 | WriteSIMMWaitingFinishReply,
39 | WriteSIMMWaitingWriteMoreReply,
40 |
41 | ElectricalTestWaitingStartReply,
42 | ElectricalTestWaitingNextStatus,
43 | ElectricalTestWaitingFirstFail,
44 | ElectricalTestWaitingSecondFail,
45 |
46 | ReadSIMMWaitingStartReply,
47 | ReadSIMMWaitingStartOffsetReply,
48 | ReadSIMMWaitingLengthReply,
49 | ReadSIMMWaitingData,
50 | ReadSIMMWaitingStatusReply,
51 |
52 | BootloaderStateAwaitingOKReply,
53 | BootloaderStateAwaitingReply,
54 | BootloaderStateAwaitingOKReplyToBootloader,
55 | BootloaderStateAwaitingReplyToBootloader,
56 | BootloaderStateAwaitingUnplug,
57 | BootloaderStateAwaitingPlug,
58 | BootloaderStateAwaitingUnplugToBootloader,
59 | BootloaderStateAwaitingPlugToBootloader,
60 |
61 | IdentificationWaitingSetSizeReply,
62 | IdentificationAwaitingOKReply,
63 | IdentificationWaitingData,
64 | IdentificationAwaitingDoneReply,
65 |
66 | BootloaderEraseProgramAwaitingStartOKReply,
67 | BootloaderEraseProgramWaitingFinishReply,
68 | BootloaderEraseProgramWaitingWriteMoreReply,
69 | BootloaderEraseProgramWaitingWriteReply,
70 |
71 | WritePortionWaitingSetSectorLayoutReply,
72 | WritePortionWaitingSectorLayoutDataReply,
73 | WritePortionWaitingSetSizeReply,
74 | WritePortionWaitingSetVerifyModeReply,
75 | WritePortionWaitingSetChipMaskReply,
76 | WritePortionWaitingSetChipMaskValueReply,
77 | WritePortionWaitingEraseReply,
78 | WritePortionWaitingEraseConfirmation,
79 | WritePortionWaitingEraseResult,
80 | WritePortionWaitingWriteAtReply,
81 |
82 | ReadFWVersionAwaitingOKReply,
83 | ReadFWVersionWaitingData,
84 | ReadFWVersionAwaitingDoneReply
85 | } ProgrammerCommandState;
86 |
87 | typedef enum ProgrammerBoardFoundState
88 | {
89 | ProgrammerBoardNotFound,
90 | ProgrammerBoardFound
91 | } ProgrammerBoardFoundState;
92 |
93 | typedef enum ProgrammerCommand
94 | {
95 | EnterWaitingMode = 0,
96 | DoElectricalTest,
97 | IdentifyChips,
98 | ReadByte,
99 | ReadChips,
100 | EraseChips,
101 | WriteChips,
102 | GetBootloaderState,
103 | EnterBootloader,
104 | EnterProgrammer,
105 | BootloaderEraseAndWriteProgram,
106 | SetSIMMLayout_AddressStraight,
107 | SetSIMMLayout_AddressShifted,
108 | SetVerifyWhileWriting,
109 | SetNoVerifyWhileWriting,
110 | ErasePortion,
111 | WriteChipsAt,
112 | ReadChipsAt,
113 | SetChipsMask,
114 | SetSectorLayout,
115 | GetFirmwareVersion
116 | } ProgrammerCommand;
117 |
118 | typedef enum ProgrammerReply
119 | {
120 | CommandReplyOK,
121 | CommandReplyError,
122 | CommandReplyInvalid
123 | } ProgrammerReply;
124 |
125 | typedef enum ComputerReadReply
126 | {
127 | ComputerReadOK,
128 | ComputerReadCancel
129 | } ComputerReadReply;
130 |
131 | typedef enum ProgrammerReadReply
132 | {
133 | ProgrammerReadOK,
134 | ProgrammerReadError,
135 | ProgrammerReadMoreData,
136 | ProgrammerReadFinished,
137 | ProgrammerReadConfirmCancel
138 | } ProgrammerReadReply;
139 |
140 | typedef enum ComputerWriteReply
141 | {
142 | ComputerWriteMore,
143 | ComputerWriteFinish,
144 | ComputerWriteCancel
145 | } ComputerWriteReply;
146 |
147 | typedef enum ProgrammerWriteReply
148 | {
149 | ProgrammerWriteOK,
150 | ProgrammerWriteError,
151 | ProgrammerWriteConfirmCancel,
152 | ProgrammerWriteVerificationError = 0x80 /* high bit */
153 | } ProgrammerWriteReply;
154 |
155 | typedef enum ProgrammerIdentifyReply
156 | {
157 | ProgrammerIdentifyDone
158 | } ProgrammerIdentifyReply;
159 |
160 | typedef enum ProgrammerElectricalTestReply
161 | {
162 | ProgrammerElectricalTestFail,
163 | ProgrammerElectricalTestDone
164 | } ProgrammerElectricalTestReply;
165 |
166 | typedef enum BootloaderStateReply
167 | {
168 | BootloaderStateInBootloader,
169 | BootloaderStateInProgrammer
170 | } BootloaderStateReply;
171 |
172 | typedef enum ProgrammerBootloaderEraseWriteReply
173 | {
174 | BootloaderWriteOK,
175 | BootloaderWriteError,
176 | BootloaderWriteConfirmCancel
177 | } ProgrammerBootloaderEraseWriteReply;
178 |
179 | typedef enum ComputerBootloaderEraseWriteRequest
180 | {
181 | ComputerBootloaderWriteMore = 0,
182 | ComputerBootloaderFinish,
183 | ComputerBootloaderCancel
184 | } ComputerBootloaderEraseWriteRequest;
185 |
186 | typedef enum ProgrammerErasePortionOfChipReply
187 | {
188 | ProgrammerErasePortionOK = 0,
189 | ProgrammerErasePortionError,
190 | ProgrammerErasePortionFinished
191 | } ProgrammerErasePortionOfChipReply;
192 |
193 | typedef enum ProgrammerGetFWVersionReply
194 | {
195 | ProgrammerGetFWVersionDone
196 | } ProgrammerGetFWVersionReply;
197 |
198 | #define PROGRAMMER_USB_VENDOR_ID 0x16D0
199 | #define PROGRAMMER_USB_DEVICE_ID 0x06AA
200 |
201 |
202 | #define WRITE_CHUNK_SIZE 1024
203 | #define READ_CHUNK_SIZE 1024
204 | #define FIRMWARE_CHUNK_SIZE 1024
205 |
206 | #define BLOCK_ERASE_SIZE (256*1024UL)
207 |
208 | static ProgrammerCommandState curState = WaitingForNextCommand;
209 |
210 | // After identifying that we're in the main program, what will be the command
211 | // we will send and the state we will be waiting in?
212 | static ProgrammerCommandState nextState = WaitingForNextCommand;
213 | static uint8_t nextSendByte = 0;
214 |
215 | static ProgrammerBoardFoundState foundState = ProgrammerBoardNotFound;
216 | static QString programmerBoardPortName;
217 |
218 | Programmer::Programmer(QObject *parent) :
219 | QObject(parent),
220 | _chipID(":/chipid/chipid.txt")
221 | {
222 | detectedDeviceRevision = 0;
223 | identifyIsForWriteAttempt = false;
224 | identifyWriteIsEntireSIMM = false;
225 | _verifyMode = VerifyAfterWrite;
226 | _verifyBadChipMask = 0;
227 | verifyArray = new QByteArray();
228 | verifyBuffer = new QBuffer(verifyArray);
229 | verifyBuffer->open(QBuffer::ReadWrite);
230 | serialPort = new QextSerialPort(QextSerialPort::EventDriven);
231 | connect(serialPort, SIGNAL(readyRead()), SLOT(dataReady()));
232 | }
233 |
234 | Programmer::~Programmer()
235 | {
236 | closePort();
237 | delete serialPort;
238 | verifyBuffer->close();
239 | delete verifyBuffer;
240 | delete verifyArray;
241 | }
242 |
243 | void Programmer::readSIMM(QIODevice *device, uint32_t len)
244 | {
245 | // We're not verifying in this case
246 | isReadVerifying = false;
247 | internalReadSIMM(device, len);
248 | }
249 |
250 | void Programmer::internalReadSIMM(QIODevice *device, uint32_t len, uint32_t offset)
251 | {
252 | readDevice = device;
253 | lenRead = 0;
254 | readOffset = offset;
255 |
256 | // Len == 0 means read the entire SIMM
257 | if (len == 0)
258 | {
259 | lenRemaining = _simmCapacity;
260 | trueLenToRead = _simmCapacity;
261 | }
262 | else if (len % READ_CHUNK_SIZE)
263 | {
264 | // We have to read a full chunk of data, so we read a little bit
265 | // past the actual length requested but only return the amount
266 | // requested.
267 | uint32_t lastExtraChunk = (len % READ_CHUNK_SIZE);
268 | lenRemaining = len - lastExtraChunk + READ_CHUNK_SIZE;
269 | trueLenToRead = len;
270 | }
271 | else // already a multiple of READ_CHUNK_SIZE, no correction needed
272 | {
273 | lenRemaining = len;
274 | trueLenToRead = len;
275 | }
276 |
277 | if (offset > 0)
278 | {
279 | startProgrammerCommand(ReadChipsAt, ReadSIMMWaitingStartOffsetReply);
280 | }
281 | else
282 | {
283 | startProgrammerCommand(ReadChips, ReadSIMMWaitingStartReply);
284 | }
285 | }
286 |
287 | void Programmer::writeToSIMM(QIODevice *device, uint8_t chipsMask)
288 | {
289 | writeDevice = device;
290 | writeChipMask = chipsMask;
291 | if (writeDevice->size() > SIMMCapacity())
292 | {
293 | curState = WaitingForNextCommand;
294 | emit writeStatusChanged(WriteFileTooBig);
295 | return;
296 | }
297 | else
298 | {
299 | lenWritten = 0;
300 | writeLenRemaining = writeDevice->size();
301 | writeOffset = 0;
302 |
303 | // Start out by identifying the chips so that we can send the correct
304 | // erase sector layout. We have to save some flags to indicate that the
305 | // identification is the start of a write. This isn't strictly necessary
306 | // for full chip erases, but I do it for consistency.
307 | identifyIsForWriteAttempt = true;
308 | identifyWriteIsEntireSIMM = true;
309 | identificationShiftCounter = 0;
310 | startProgrammerCommand(SetSIMMLayout_AddressStraight, IdentificationWaitingSetSizeReply);
311 | }
312 | }
313 |
314 | void Programmer::writeToSIMM(QIODevice *device, uint32_t startOffset, uint32_t length, uint8_t chipsMask)
315 | {
316 | writeDevice = device;
317 | writeChipMask = chipsMask;
318 | if ((writeDevice->size() > SIMMCapacity()) ||
319 | (startOffset + length > SIMMCapacity()))
320 | {
321 | curState = WaitingForNextCommand;
322 | emit writeStatusChanged(WriteFileTooBig);
323 | return;
324 | }
325 | else if ((startOffset % BLOCK_ERASE_SIZE) || (length % BLOCK_ERASE_SIZE))
326 | {
327 | curState = WaitingForNextCommand;
328 | emit writeStatusChanged(WriteEraseBlockWrongSize);
329 | return;
330 | }
331 | else
332 | {
333 | lenWritten = 0;
334 | writeLenRemaining = writeDevice->size() - startOffset;
335 | if (writeLenRemaining > length)
336 | {
337 | writeLenRemaining = length;
338 | }
339 | device->seek(startOffset);
340 | writeOffset = startOffset;
341 | writeLength = length;
342 |
343 | // Start out by identifying the chips so that we can send the correct
344 | // erase sector layout. We have to save some flags to indicate that the
345 | // identification is the start of a write.
346 | identifyIsForWriteAttempt = true;
347 | identifyWriteIsEntireSIMM = false;
348 | identificationShiftCounter = 0;
349 | startProgrammerCommand(SetSIMMLayout_AddressStraight, IdentificationWaitingSetSizeReply);
350 | }
351 | }
352 |
353 | void Programmer::sendByte(uint8_t b)
354 | {
355 | serialPort->write((const char *)&b, 1);
356 | }
357 |
358 | void Programmer::sendWord(uint32_t w)
359 | {
360 | sendByte((w >> 0) & 0xFF);
361 | sendByte((w >> 8) & 0xFF);
362 | sendByte((w >> 16) & 0xFF);
363 | sendByte((w >> 24) & 0xFF);
364 | }
365 |
366 | uint8_t Programmer::readByte()
367 | {
368 | uint8_t returnVal;
369 | serialPort->read((char *)&returnVal, 1);
370 | // TODO: Error checking if read fails?
371 | return returnVal;
372 | }
373 |
374 | void Programmer::dataReady()
375 | {
376 | while (!serialPort->atEnd())
377 | {
378 | handleChar(readByte());
379 | }
380 | }
381 |
382 | void Programmer::handleChar(uint8_t c)
383 | {
384 | switch (curState)
385 | {
386 | case WaitingForNextCommand:
387 | // Not expecting anything. Ignore it.
388 | break;
389 |
390 | // Expecting reply after we told the programmer the sector layout to use.
391 | // Go ahead and send the sector layout even if we're doing a full erase.
392 | // It makes the code more maintainable and opens up possibilities for the future.
393 | case WriteSIMMWaitingSetSectorLayoutReply:
394 | case WritePortionWaitingSetSectorLayoutReply:
395 | switch (c)
396 | {
397 | case CommandReplyOK:
398 | // We are talking with firmware that supports receiving sector layout data! Yay!
399 | for (int i = 0; i < sectorGroups.count(); i++)
400 | {
401 | // Send the count of sectors in this group
402 | sendWord(sectorGroups[i].first);
403 | // Send the size of each sector in this group
404 | sendWord(sectorGroups[i].second);
405 | }
406 | // Send a 0 to terminate the list of sector groups.
407 | sendWord(0);
408 | // This should cause the programmer to respond back to us with a yea or nay.
409 | curState = (curState == WriteSIMMWaitingSetSectorLayoutReply) ?
410 | WriteSIMMWaitingSectorLayoutDataReply : WritePortionWaitingSectorLayoutDataReply;
411 | break;
412 | case CommandReplyInvalid:
413 | case CommandReplyError:
414 | default:
415 | // If this command fails, just silently ignore the error and move
416 | // onto setting the SIMM address unlock pattern instead.
417 | uint8_t setLayoutCommand = (SIMMChip() == SIMM_TSOP_x8) ?
418 | SetSIMMLayout_AddressShifted : SetSIMMLayout_AddressStraight;
419 | ProgrammerCommandState newState = (curState == WriteSIMMWaitingSetSectorLayoutReply) ?
420 | WriteSIMMWaitingSetSizeReply : WritePortionWaitingSetSizeReply;
421 | startProgrammerCommand(setLayoutCommand, newState);
422 | }
423 | break;
424 |
425 | // Expecting reply after the programmer allowed us to send the sector layout
426 | case WriteSIMMWaitingSectorLayoutDataReply:
427 | case WritePortionWaitingSectorLayoutDataReply:
428 | switch (c)
429 | {
430 | case CommandReplyOK: {
431 | // All good! Now move onto setting the SIMM address unlock pattern
432 | uint8_t setLayoutCommand = (SIMMChip() == SIMM_TSOP_x8) ?
433 | SetSIMMLayout_AddressShifted : SetSIMMLayout_AddressStraight;
434 | ProgrammerCommandState newState = (curState == WriteSIMMWaitingSectorLayoutDataReply) ?
435 | WriteSIMMWaitingSetSizeReply : WritePortionWaitingSetSizeReply;
436 | startProgrammerCommand(setLayoutCommand, newState);
437 | break;
438 | }
439 | case CommandReplyInvalid:
440 | case CommandReplyError:
441 | // Error after trying to send the sector layout. The firmware clearly supports the command,
442 | // so we need to return an error.
443 | qDebug() << "Error reply sending erase sector layout.";
444 | curState = WaitingForNextCommand;
445 | closePort();
446 | emit writeStatusChanged(WriteError);
447 | break;
448 | }
449 |
450 | break;
451 |
452 | // Expecting reply after we told the programmer the size of SIMM to expect
453 | case WriteSIMMWaitingSetSizeReply:
454 | case WritePortionWaitingSetSizeReply:
455 | switch (c)
456 | {
457 | case CommandReplyOK:
458 | // If we got an OK reply, we're good to go. Next, check for the
459 | // "verify while writing" capability if needed...
460 |
461 | uint8_t verifyCommand;
462 | if (verifyMode() == VerifyWhileWriting)
463 | {
464 | verifyCommand = SetVerifyWhileWriting;
465 | }
466 | else
467 | {
468 | verifyCommand = SetNoVerifyWhileWriting;
469 | }
470 |
471 | if (curState == WriteSIMMWaitingSetSizeReply)
472 | {
473 | curState = WriteSIMMWaitingSetVerifyModeReply;
474 | }
475 | else if (curState == WritePortionWaitingSetSizeReply)
476 | {
477 | curState = WritePortionWaitingSetVerifyModeReply;
478 | }
479 | sendByte(verifyCommand);
480 | break;
481 | case CommandReplyInvalid:
482 | case CommandReplyError:
483 | // If we got an error reply, we MAY still be OK unless we were
484 | // requesting the large SIMM type, in which case the firmware
485 | // doesn't support the large SIMM type so the user needs to know.
486 | if (SIMMChip() != SIMM_PLCC_x8)
487 | {
488 | // Uh oh -- this is an old firmware that doesn't support a big
489 | // SIMM. Let the caller know that the programmer board needs a
490 | // firmware update.
491 | qDebug() << "Programmer board needs firmware update.";
492 | curState = WaitingForNextCommand;
493 | closePort();
494 | emit writeStatusChanged(WriteNeedsFirmwareUpdateBiggerSIMM);
495 | }
496 | else
497 | {
498 | // Error reply, but we're writing a small SIMM, so the firmware
499 | // doesn't need updating -- it just didn't know how to handle
500 | // the "set size" command. But that's OK -- it only supports
501 | // the size we requested, so nothing's wrong.
502 |
503 | // So...check for the "verify while writing" capability if needed.
504 | uint8_t verifyCommand;
505 | if (verifyMode() == VerifyWhileWriting)
506 | {
507 | verifyCommand = SetVerifyWhileWriting;
508 | }
509 | else
510 | {
511 | verifyCommand = SetNoVerifyWhileWriting;
512 | }
513 |
514 | if (curState == WriteSIMMWaitingSetSizeReply)
515 | {
516 | curState = WriteSIMMWaitingSetVerifyModeReply;
517 | }
518 | else if (curState == WritePortionWaitingSetSizeReply)
519 | {
520 | curState = WritePortionWaitingSetVerifyModeReply;
521 | }
522 | sendByte(verifyCommand);
523 | }
524 | break;
525 | }
526 |
527 | break;
528 |
529 | // Expecting reply from programmer after we told it to verify during write
530 | // (or not to verify during write)
531 | case WriteSIMMWaitingSetVerifyModeReply:
532 | case WritePortionWaitingSetVerifyModeReply:
533 | switch (c)
534 | {
535 | case CommandReplyOK:
536 | // If we got an OK reply, we're good. Now try to set the chip mask.
537 | if (curState == WriteSIMMWaitingSetVerifyModeReply)
538 | {
539 | sendByte(SetChipsMask);
540 | curState = WriteSIMMWaitingSetChipMaskReply;
541 | }
542 | else if (curState == WritePortionWaitingSetVerifyModeReply)
543 | {
544 | sendByte(SetChipsMask);
545 | curState = WritePortionWaitingSetChipMaskReply;
546 | }
547 | break;
548 | case CommandReplyInvalid:
549 | case CommandReplyError:
550 | // If we got an error reply, we MAY still be OK unless we were
551 | // asking to verify while writing, in which case the firmware
552 | // doesn't support verify during write so the user needs to know.
553 | if (verifyMode() == VerifyWhileWriting)
554 | {
555 | // Uh oh -- this is an old firmware that doesn't support verify
556 | // while write. Let the caller know that the programmer board
557 | // needs a firmware update.
558 | qDebug() << "Programmer board needs firmware update.";
559 | curState = WaitingForNextCommand;
560 | closePort();
561 | emit writeStatusChanged(WriteNeedsFirmwareUpdateVerifyWhileWrite);
562 | }
563 | else
564 | {
565 | // Error reply, but we're not trying to verify while writing, so
566 | // the firmware doesn't need updating -- it just didn't know how to handle
567 | // the "set verify mode" command. But that's OK -- we don't need
568 | // that command if we're not verifying while writing.
569 |
570 | // So move onto the next thing to try.
571 | if (curState == WriteSIMMWaitingSetVerifyModeReply)
572 | {
573 | sendByte(SetChipsMask);
574 | curState = WriteSIMMWaitingSetChipMaskReply;
575 | }
576 | else if (curState == WritePortionWaitingSetVerifyModeReply)
577 | {
578 | sendByte(SetChipsMask);
579 | curState = WritePortionWaitingSetChipMaskReply;
580 | }
581 | }
582 | break;
583 | }
584 |
585 | break;
586 |
587 | case WriteSIMMWaitingSetChipMaskReply:
588 | case WritePortionWaitingSetChipMaskReply:
589 | switch (c)
590 | {
591 | case CommandReplyOK:
592 | // OK, now we can send the chip mask and move onto the next state
593 | sendByte(writeChipMask);
594 | if (curState == WriteSIMMWaitingSetChipMaskReply)
595 | {
596 | curState = WriteSIMMWaitingSetChipMaskValueReply;
597 | }
598 | else if (curState == WritePortionWaitingSetChipMaskReply)
599 | {
600 | curState = WritePortionWaitingSetChipMaskValueReply;
601 | }
602 | break;
603 | case CommandReplyInvalid:
604 | case CommandReplyError:
605 | // Error reply. If we're trying to set a mask of 0x0F, no error, it
606 | // just means the firmware's out of date and doesn't support setting
607 | // custom chip masks. Ignore and move on.
608 | if (writeChipMask == 0x0F)
609 | {
610 | // OK, erase the SIMM and get the ball rolling.
611 | // Special case: Send out notification we are starting an erase command.
612 | // I don't have any hooks into the process between now and the erase reply.
613 | emit writeStatusChanged(WriteErasing);
614 | if (curState == WriteSIMMWaitingSetChipMaskReply)
615 | {
616 | sendByte(EraseChips);
617 | curState = WriteSIMMWaitingEraseReply;
618 | }
619 | else if (curState == WritePortionWaitingSetChipMaskReply)
620 | {
621 | sendByte(ErasePortion);
622 | curState = WritePortionWaitingEraseReply;
623 | }
624 | }
625 | else
626 | {
627 | // Uh oh -- this is an old firmware that doesn't support custom
628 | // chip masks. Let the caller know that the programmer board
629 | // needs a firmware update.
630 | qDebug() << "Programmer board needs firmware update.";
631 | curState = WaitingForNextCommand;
632 | closePort();
633 | emit writeStatusChanged(WriteNeedsFirmwareUpdateIndividualChips);
634 | }
635 | break;
636 | }
637 |
638 | break;
639 |
640 | case WriteSIMMWaitingSetChipMaskValueReply:
641 | case WritePortionWaitingSetChipMaskValueReply:
642 | switch (c)
643 | {
644 | case CommandReplyOK:
645 | // OK, erase the SIMM and get the ball rolling.
646 | // Special case: Send out notification we are starting an erase command.
647 | // I don't have any hooks into the process between now and the erase reply.
648 | emit writeStatusChanged(WriteErasing);
649 | if (curState == WriteSIMMWaitingSetChipMaskValueReply)
650 | {
651 | sendByte(EraseChips);
652 | curState = WriteSIMMWaitingEraseReply;
653 | }
654 | else if (curState == WritePortionWaitingSetChipMaskValueReply)
655 | {
656 | sendByte(ErasePortion);
657 | curState = WritePortionWaitingEraseReply;
658 | }
659 | break;
660 | case CommandReplyInvalid:
661 | case CommandReplyError:
662 | // Error after trying to set the value.
663 | qDebug() << "Error reply setting chip mask.";
664 | curState = WaitingForNextCommand;
665 | closePort();
666 | emit writeStatusChanged(WriteError);
667 | break;
668 | }
669 |
670 | break;
671 |
672 | // Expecting reply from programmer after we told it to erase the chip
673 | case WriteSIMMWaitingEraseReply:
674 | {
675 | switch (c)
676 | {
677 | case CommandReplyOK:
678 | sendByte(WriteChips);
679 | curState = WriteSIMMWaitingWriteReply;
680 | qDebug() << "Chips erased. Now asking to start writing...";
681 | emit writeStatusChanged(WriteEraseComplete);
682 | emit writeTotalLengthChanged(writeLenRemaining);
683 | emit writeCompletionLengthChanged(lenWritten);
684 | break;
685 | case CommandReplyError:
686 | qDebug() << "Error erasing chips.";
687 | curState = WaitingForNextCommand;
688 | closePort();
689 | emit writeStatusChanged(WriteEraseFailed);
690 | break;
691 | }
692 | break;
693 | }
694 |
695 | case WritePortionWaitingEraseReply:
696 | {
697 | switch (c)
698 | {
699 | case CommandReplyOK:
700 | sendWord(writeOffset);
701 | sendWord(writeLength);
702 | qDebug("Sending %u, %u", writeOffset, writeLength);
703 | curState = WritePortionWaitingEraseConfirmation;
704 | qDebug() << "Sent erase positions, waiting for reply...";
705 | break;
706 | case CommandReplyError:
707 | // Uh oh -- this is an old firmware that doesn't support verify
708 | // while write. Let the caller know that the programmer board
709 | // needs a firmware update.
710 | qDebug() << "Programmer board needs firmware update.";
711 | curState = WaitingForNextCommand;
712 | closePort();
713 | emit writeStatusChanged(WriteNeedsFirmwareUpdateErasePortion);
714 | break;
715 | }
716 | break;
717 | }
718 |
719 | case WritePortionWaitingEraseConfirmation:
720 | {
721 | switch (c)
722 | {
723 | case ProgrammerErasePortionOK:
724 | curState = WritePortionWaitingEraseResult;
725 | break;
726 | case ProgrammerErasePortionError:
727 | // Programmer didn't like the position/length we gave it
728 | qDebug() << "Programmer didn't like erase pos/length.";
729 | curState = WaitingForNextCommand;
730 | closePort();
731 | emit writeStatusChanged(WriteEraseFailed);
732 | break;
733 | }
734 |
735 | break;
736 | }
737 |
738 | case WritePortionWaitingEraseResult:
739 | {
740 | switch (c)
741 | {
742 | case ProgrammerErasePortionFinished:
743 | // we're done erasing, now it's time to write the data
744 | // starting at where we wanted to flash to
745 | sendByte(WriteChipsAt);
746 | curState = WritePortionWaitingWriteAtReply;
747 | qDebug() << "Chips partially erased. Now asking to start writing...";
748 | emit writeStatusChanged(WriteEraseComplete);
749 | break;
750 | case ProgrammerErasePortionError:
751 | // Programmer failed to erase
752 | qDebug() << "Programmer had error erasing.";
753 | curState = WaitingForNextCommand;
754 | closePort();
755 | emit writeStatusChanged(WriteEraseFailed);
756 | break;
757 | }
758 |
759 | break;
760 | }
761 |
762 | case WritePortionWaitingWriteAtReply:
763 | {
764 | switch (c)
765 | {
766 | case CommandReplyOK:
767 | sendWord(writeOffset);
768 | qDebug() << "Sending" << writeOffset;
769 | curState = WriteSIMMWaitingWriteReply;
770 | emit writeTotalLengthChanged(writeLenRemaining);
771 | emit writeCompletionLengthChanged(lenWritten);
772 | qDebug() << "Partial write command accepted, sending offset...";
773 | break;
774 | case CommandReplyError:
775 | case CommandReplyInvalid:
776 | default:
777 | // Programmer failed to erase
778 | qDebug() << "Programmer didn't accept 'write at' command.";
779 | curState = WaitingForNextCommand;
780 | closePort();
781 | emit writeStatusChanged(WriteError);
782 | break;
783 | }
784 |
785 | break;
786 | }
787 |
788 | // Expecting reply from programmer after we sent a chunk of data to write
789 | // (or after we first told it we're going to start writing)
790 | case WriteSIMMWaitingWriteReply:
791 | // This is a special case in the protocol for efficiency.
792 | if (c & ProgrammerWriteVerificationError)
793 | {
794 | _verifyBadChipMask = c & ~ProgrammerWriteVerificationError;
795 | qDebug() << "Verification error during write.";
796 | curState = WaitingForNextCommand;
797 | closePort();
798 | emit writeStatusChanged(WriteVerificationFailure);
799 | break;
800 | }
801 | else
802 | {
803 | switch (c)
804 | {
805 | case CommandReplyOK:
806 | // We're in write SIMM mode. Now ask to start writing
807 | if (writeLenRemaining > 0)
808 | {
809 | sendByte(ComputerWriteMore);
810 | curState = WriteSIMMWaitingWriteMoreReply;
811 | qDebug() << "Write more..." << writeLenRemaining << "remaining.";
812 | }
813 | else
814 | {
815 | sendByte(ComputerWriteFinish);
816 | curState = WriteSIMMWaitingFinishReply;
817 | qDebug() << "Finished writing. Sending write finish command...";
818 | }
819 | break;
820 | case CommandReplyError:
821 | qDebug() << "Error entering write mode.";
822 | curState = WaitingForNextCommand;
823 | closePort();
824 | emit writeStatusChanged(WriteError);
825 | break;
826 | }
827 | }
828 |
829 | break;
830 |
831 | // Expecting reply from programmer after we requested to write another data chunk
832 | case WriteSIMMWaitingWriteMoreReply:
833 | {
834 | qDebug() << "Write more reply:" << c;
835 | switch (c)
836 | {
837 | case ProgrammerWriteOK:
838 | {
839 | qDebug() << "Programmer replied OK to send 1024 bytes of data! Sending...";
840 | // Write the next chunk of data to the SIMM...
841 |
842 | int chunkSize = WRITE_CHUNK_SIZE;
843 | if (writeLenRemaining < WRITE_CHUNK_SIZE)
844 | {
845 | chunkSize = writeLenRemaining;
846 | }
847 |
848 | // Read the chunk from the file!
849 | QByteArray thisChunk = writeDevice->read(chunkSize);
850 |
851 | // If it isn't a WRITE_CHUNK_SIZE chunk, pad the rest of it with 0xFFs (unprogrammed bytes)
852 | // so the total chunk size is WRITE_CHUNK_SIZE, since that's what the programmer board expects.
853 | for (int x = writeLenRemaining; x < WRITE_CHUNK_SIZE; x++)
854 | {
855 | thisChunk.append(0xFF);
856 | }
857 |
858 | // Write the chunk out (it's asynchronous so will return immediately)
859 | serialPort->write(thisChunk);
860 |
861 | // OK, now we're waiting to hear back from the programmer on the result
862 | qDebug() << "Waiting for status reply...";
863 | curState = WriteSIMMWaitingWriteReply;
864 | writeLenRemaining -= chunkSize;
865 | lenWritten += chunkSize;
866 | emit writeCompletionLengthChanged(lenWritten);
867 | break;
868 | }
869 | case ProgrammerWriteError:
870 | default:
871 | qDebug() << "Error writing to chips.";
872 | curState = WaitingForNextCommand;
873 | closePort();
874 | emit writeStatusChanged(WriteError);
875 | break;
876 | }
877 | break;
878 | }
879 |
880 | // Expecting reply from programmer after we told it we're done writing
881 | case WriteSIMMWaitingFinishReply:
882 | switch (c)
883 | {
884 | case ProgrammerWriteOK:
885 | if (verifyMode() == VerifyAfterWrite)
886 | {
887 | isReadVerifying = true;
888 |
889 | // Ensure the verify buffer is empty
890 | verifyArray->clear();
891 | verifyBuffer->seek(0);
892 | verifyLength = lenWritten;
893 |
894 | // Start reading from the SIMM now!
895 | emit writeStatusChanged(WriteVerifying);
896 | internalReadSIMM(verifyBuffer, writeDevice->size());
897 | }
898 | else
899 | {
900 | curState = WaitingForNextCommand;
901 | qDebug() << "Write success at end";
902 | closePort();
903 |
904 | // Emit the correct signal based on how we finished
905 | if (verifyMode() == NoVerification)
906 | {
907 | emit writeStatusChanged(WriteCompleteNoVerify);
908 | }
909 | else
910 | {
911 | emit writeStatusChanged(WriteCompleteVerifyOK);
912 | }
913 | }
914 |
915 | break;
916 | case ProgrammerWriteError:
917 | default:
918 | qDebug() << "Write failure at end";
919 | curState = WaitingForNextCommand;
920 | closePort();
921 | emit writeStatusChanged(WriteError);
922 | break;
923 | }
924 |
925 | break;
926 |
927 | // ELECTRICAL TEST STATE HANDLERS
928 |
929 | // Expecting reply from programmer after we told it to run an electrical test
930 | case ElectricalTestWaitingStartReply:
931 | switch (c)
932 | {
933 | case CommandReplyOK:
934 | curState = ElectricalTestWaitingNextStatus;
935 | emit electricalTestStatusChanged(ElectricalTestStarted);
936 | electricalTestErrorCounter = 0;
937 | break;
938 | case CommandReplyError:
939 | case CommandReplyInvalid:
940 | default:
941 | curState = WaitingForNextCommand;
942 | closePort();
943 | emit electricalTestStatusChanged(ElectricalTestCouldntStart);
944 | }
945 | break;
946 |
947 | // Expecting info from programmer about the electrical test in progress
948 | // (Either that it's done or that it found a failure)
949 | case ElectricalTestWaitingNextStatus:
950 | switch (c)
951 | {
952 | case ProgrammerElectricalTestDone:
953 | curState = WaitingForNextCommand;
954 | closePort();
955 | if (electricalTestErrorCounter > 0)
956 | {
957 | emit electricalTestStatusChanged(ElectricalTestFailed);
958 | }
959 | else
960 | {
961 | emit electricalTestStatusChanged(ElectricalTestPassed);
962 | }
963 | break;
964 | case ProgrammerElectricalTestFail:
965 | electricalTestErrorCounter++;
966 | curState = ElectricalTestWaitingFirstFail;
967 | break;
968 | }
969 | break;
970 | // Expecting electrical test fail location #1
971 | case ElectricalTestWaitingFirstFail:
972 | electricalTestFirstErrorLoc = c;
973 | curState = ElectricalTestWaitingSecondFail;
974 | break;
975 | // Expecting electrical test fail location #2
976 | case ElectricalTestWaitingSecondFail:
977 | emit electricalTestFailLocation(electricalTestFirstErrorLoc, c);
978 | curState = ElectricalTestWaitingNextStatus;
979 | break;
980 |
981 | // READ SIMM STATE HANDLERS
982 |
983 | // Expecting reply after we told the programmer to start reading
984 | case ReadSIMMWaitingStartReply:
985 | case ReadSIMMWaitingStartOffsetReply:
986 | switch (c)
987 | {
988 | case CommandReplyOK:
989 |
990 | if (!isReadVerifying)
991 | {
992 | emit readStatusChanged(ReadStarting);
993 | }
994 | else
995 | {
996 | emit writeStatusChanged(WriteVerifyStarting);
997 | }
998 |
999 | curState = ReadSIMMWaitingLengthReply;
1000 |
1001 | // Send the length requesting to be read (and offset if needed)
1002 | if (c == ReadSIMMWaitingStartOffsetReply)
1003 | {
1004 | sendWord(readOffset);
1005 | }
1006 | sendWord(lenRemaining);
1007 |
1008 | // Now wait for the go-ahead from the programmer's side
1009 | break;
1010 | case CommandReplyError:
1011 | case CommandReplyInvalid:
1012 | default:
1013 | curState = WaitingForNextCommand;
1014 | closePort();
1015 | if (!isReadVerifying)
1016 | {
1017 | emit readStatusChanged(ReadError);
1018 | }
1019 | else
1020 | {
1021 | // Ensure the verify buffer is empty if we were verifying
1022 | verifyArray->clear();
1023 | verifyBuffer->seek(0);
1024 | emit writeStatusChanged(WriteVerifyError);
1025 | }
1026 | break;
1027 | }
1028 | break;
1029 |
1030 | // Expecting reply after we gave the programmer a length to read
1031 | case ReadSIMMWaitingLengthReply:
1032 | switch (c)
1033 | {
1034 | case ProgrammerReadOK:
1035 | curState = ReadSIMMWaitingData;
1036 | if (!isReadVerifying)
1037 | {
1038 | emit readTotalLengthChanged(lenRemaining);
1039 | emit readCompletionLengthChanged(0);
1040 | }
1041 | else
1042 | {
1043 | emit writeVerifyTotalLengthChanged(lenRemaining);
1044 | emit writeVerifyCompletionLengthChanged(0);
1045 | }
1046 | readChunkLenRemaining = READ_CHUNK_SIZE;
1047 | break;
1048 | case ProgrammerReadError:
1049 | default:
1050 | curState = WaitingForNextCommand;
1051 | closePort();
1052 | if (!isReadVerifying)
1053 | {
1054 | emit readStatusChanged(ReadError);
1055 | }
1056 | else
1057 | {
1058 | // Ensure the verify buffer is empty if we were verifying
1059 | verifyArray->clear();
1060 | verifyBuffer->seek(0);
1061 | emit writeStatusChanged(WriteVerifyError);
1062 | }
1063 | break;
1064 | }
1065 | break;
1066 |
1067 | // Expecting a chunk of data back from the programmer
1068 | case ReadSIMMWaitingData:
1069 | // Only keep adding to the readback if we need to
1070 | if (lenRead < trueLenToRead)
1071 | {
1072 | readDevice->write((const char *)&c, 1);
1073 | }
1074 |
1075 | lenRead++;
1076 | if (--readChunkLenRemaining == 0)
1077 | {
1078 | if (!isReadVerifying)
1079 | {
1080 | emit readCompletionLengthChanged(lenRead);
1081 | }
1082 | else
1083 | {
1084 | emit writeVerifyCompletionLengthChanged(lenRead);
1085 | }
1086 | qDebug() << "Received a chunk of data";
1087 | sendByte(ComputerReadOK);
1088 | curState = ReadSIMMWaitingStatusReply;
1089 | }
1090 | break;
1091 |
1092 | // Expecting status reply from programmer after we confirmed reception of
1093 | // previous chunk of data
1094 | case ReadSIMMWaitingStatusReply:
1095 | switch (c)
1096 | {
1097 | case ProgrammerReadFinished:
1098 | curState = WaitingForNextCommand;
1099 | closePort();
1100 | if (!isReadVerifying)
1101 | {
1102 | emit readStatusChanged(ReadComplete);
1103 | }
1104 | else
1105 | {
1106 | doVerifyAfterWriteCompare();
1107 | }
1108 | break;
1109 | case ProgrammerReadConfirmCancel:
1110 | curState = WaitingForNextCommand;
1111 | closePort();
1112 | if (!isReadVerifying)
1113 | {
1114 | emit readStatusChanged(ReadCancelled);
1115 | }
1116 | else
1117 | {
1118 | // Ensure the verify buffer is empty if we were verifying
1119 | verifyArray->clear();
1120 | verifyBuffer->seek(0);
1121 | emit writeStatusChanged(WriteVerifyCancelled);
1122 | }
1123 | break;
1124 | case ProgrammerReadMoreData:
1125 | curState = ReadSIMMWaitingData;
1126 | readChunkLenRemaining = READ_CHUNK_SIZE;
1127 | break;
1128 | }
1129 |
1130 | break;
1131 |
1132 | // BOOTLOADER STATE HANDLERS
1133 |
1134 | // Expecting reply after we asked for bootloader state (original request is
1135 | // to end up in programmer mode)
1136 | case BootloaderStateAwaitingOKReply:
1137 | if (c == CommandReplyOK)
1138 | {
1139 | // Good to go, now we're waiting for the "in programmer" or "in bootloader" reply.
1140 | curState = BootloaderStateAwaitingReply;
1141 | }
1142 | else
1143 | {
1144 | curState = WaitingForNextCommand;
1145 | qDebug() << "Unable to enter programmer mode";
1146 | // TODO: Error out somehow
1147 | }
1148 | break;
1149 |
1150 | // Expecting bootloader state after request was confirmed (original request
1151 | // is to end up in programmer mode)
1152 | case BootloaderStateAwaitingReply:
1153 | switch (c)
1154 | {
1155 | case BootloaderStateInBootloader:
1156 | // Oops! We're in the bootloader. Better change over to the programmer.
1157 | qDebug() << "We're in the bootloader, so sending an \"enter programmer\" request.";
1158 | emit startStatusChanged(ProgrammerInitializing);
1159 | sendByte(EnterProgrammer);
1160 | serialPort->flush();
1161 | closePort();
1162 |
1163 | // Now wait for it to reconnect
1164 | curState = BootloaderStateAwaitingUnplug;
1165 | break;
1166 | case BootloaderStateInProgrammer:
1167 | // Good to go...
1168 | // So change to the next state and send out the next command
1169 | // to begin whatever sequence of events we expected.
1170 | qDebug() << "Already in programmer. Good! Do the command now...";
1171 | emit startStatusChanged(ProgrammerInitialized);
1172 | curState = nextState;
1173 | sendByte(nextSendByte);
1174 | break;
1175 | // TODO: Otherwise, raise an error?
1176 | }
1177 | break;
1178 |
1179 | // Expecting reply after we asked for bootloader state (original request is
1180 | // to end up in bootloader mode)
1181 | case BootloaderStateAwaitingOKReplyToBootloader:
1182 | if (c == CommandReplyOK)
1183 | {
1184 | // Good to go, now we're waiting for the "in programmer" or "in bootloader" reply.
1185 | curState = BootloaderStateAwaitingReplyToBootloader;
1186 | }
1187 | else
1188 | {
1189 | curState = WaitingForNextCommand;
1190 | qDebug() << "Unable to enter bootloader mode";
1191 | // TODO: Error out somehow
1192 | }
1193 | break;
1194 |
1195 | // Expecting bootloader state after request was confirmed (original request
1196 | // is to end up in bootloader mode)
1197 | case BootloaderStateAwaitingReplyToBootloader:
1198 | switch (c)
1199 | {
1200 | case BootloaderStateInProgrammer:
1201 | // Oops! We're in the programmer. Better change over to the bootloader.
1202 | qDebug() << "We're in the programmer, so sending an \"enter bootloader\" request.";
1203 | emit startStatusChanged(ProgrammerInitializing);
1204 | sendByte(EnterBootloader);
1205 | serialPort->flush();
1206 | closePort();
1207 |
1208 | // Now wait for it to reconnect
1209 | curState = BootloaderStateAwaitingUnplugToBootloader;
1210 | break;
1211 | case BootloaderStateInBootloader:
1212 | // Good to go...
1213 | // So change to the next state and send out the next command
1214 | // to begin whatever sequence of events we expected.
1215 | qDebug() << "Already in bootloader. Good! Do the command now...";
1216 | emit startStatusChanged(ProgrammerInitialized);
1217 | curState = nextState;
1218 | sendByte(nextSendByte);
1219 | break;
1220 | // TODO: Otherwise, raise an error?
1221 | }
1222 | break;
1223 |
1224 | // IDENTIFICATION STATE HANDLERS
1225 |
1226 | // // Expecting reply after we told the programmer what size of SIMM to use
1227 | case IdentificationWaitingSetSizeReply:
1228 | switch (c)
1229 | {
1230 | case CommandReplyOK:
1231 | // If we got an OK reply, we're ready to go, so start...
1232 | sendByte(IdentifyChips);
1233 | curState = IdentificationAwaitingOKReply;
1234 | break;
1235 | case CommandReplyInvalid:
1236 | case CommandReplyError:
1237 | // If we got an error reply, we MAY still be OK unless we were
1238 | // requesting the large SIMM type, in which case the firmware
1239 | // doesn't support the large SIMM type so the user needs to know.
1240 | if (SIMMChip() != SIMM_PLCC_x8)
1241 | {
1242 | if (!identifyIsForWriteAttempt)
1243 | {
1244 | // Uh oh -- this is an old firmware that doesn't support a big
1245 | // SIMM. Let the caller know that the programmer board needs a
1246 | // firmware update.
1247 | qDebug() << "Programmer board needs firmware update.";
1248 | curState = WaitingForNextCommand;
1249 | closePort();
1250 | emit identificationStatusChanged(IdentificationNeedsFirmwareUpdate);
1251 | }
1252 | else
1253 | {
1254 | // Don't inhibit writes if we failed to identify. Just assume an empty/unknown
1255 | // sector layout and continue on
1256 | sectorGroups.clear();
1257 | if (identifyWriteIsEntireSIMM)
1258 | {
1259 | startProgrammerCommand(SetSectorLayout, WriteSIMMWaitingSetSectorLayoutReply);
1260 | }
1261 | else
1262 | {
1263 | startProgrammerCommand(SetSectorLayout, WritePortionWaitingSetSectorLayoutReply);
1264 | }
1265 | }
1266 | }
1267 | else
1268 | {
1269 | // Error reply, but we're identifying a small SIMM, so the firmware
1270 | // doesn't need updating -- it just didn't know how to handle
1271 | // the "set size" command. But that's OK -- it only supports
1272 | // the size we requested, so nothing's wrong.
1273 | sendByte(IdentifyChips);
1274 | curState = IdentificationAwaitingOKReply;
1275 | }
1276 | break;
1277 | }
1278 | break;
1279 |
1280 | // Expecting reply after we asked to identify chips
1281 | case IdentificationAwaitingOKReply:
1282 | if (c == CommandReplyOK)
1283 | {
1284 | // Good to go, now waiting for identification data
1285 | if (identificationShiftCounter == 0 && !identifyIsForWriteAttempt)
1286 | { // If this is the first identification attempt, emit the signal
1287 | emit identificationStatusChanged(IdentificationStarting);
1288 | }
1289 | curState = IdentificationWaitingData;
1290 | identificationReadCounter = 0;
1291 | }
1292 | else
1293 | {
1294 | // Error -- close the port, we're done!
1295 | closePort();
1296 | if (!identifyIsForWriteAttempt)
1297 | {
1298 | emit identificationStatusChanged(IdentificationError);
1299 | }
1300 | else
1301 | {
1302 | emit writeStatusChanged(WriteError);
1303 | }
1304 | curState = WaitingForNextCommand;
1305 | }
1306 | break;
1307 |
1308 | // Expecting device/manufacturer info about the chips
1309 | case IdentificationWaitingData:
1310 | if (identificationReadCounter & 1) // device ID?
1311 | {
1312 | chipDeviceIDs[identificationShiftCounter][identificationReadCounter/2] = c;
1313 | }
1314 | else // manufacturer ID?
1315 | {
1316 | chipManufacturerIDs[identificationShiftCounter][identificationReadCounter/2] = c;
1317 | }
1318 |
1319 | // All done?
1320 | if (++identificationReadCounter >= 8)
1321 | {
1322 | curState = IdentificationAwaitingDoneReply;
1323 | }
1324 | break;
1325 |
1326 | // Expecting final done confirmation after receiving all device/manufacturer info
1327 | case IdentificationAwaitingDoneReply:
1328 | if (++identificationShiftCounter >= 2)
1329 | {
1330 | if (!identifyIsForWriteAttempt)
1331 | {
1332 | curState = WaitingForNextCommand;
1333 | closePort();
1334 | if (c == ProgrammerIdentifyDone)
1335 | {
1336 | emit identificationStatusChanged(IdentificationComplete);
1337 | }
1338 | else
1339 | {
1340 | emit identificationStatusChanged(IdentificationError);
1341 | }
1342 | }
1343 | else
1344 | {
1345 | // This was for a write attempt and we got the ID data. Now parse it
1346 | // to try to figure out the erase sector layout. If we can't find anything,
1347 | // fall back to empty erase sector info.
1348 | sectorGroups.clear();
1349 |
1350 | // We have to convert the ID info into a format that is usable by ChipID
1351 | QList manufacturersStraight;
1352 | QList devicesStraight;
1353 | QList manufacturersShifted;
1354 | QList devicesShifted;
1355 | for (int i = 0; i < 4; i++)
1356 | {
1357 | manufacturersStraight << chipManufacturerIDs[0][i];
1358 | devicesStraight << chipDeviceIDs[0][i];
1359 | manufacturersShifted << chipManufacturerIDs[1][i];
1360 | devicesShifted << chipDeviceIDs[1][i];
1361 | }
1362 |
1363 | // Now ask ChipID to tell us what we have
1364 | QList chipInfo;
1365 | if (_chipID.findChips(manufacturersStraight, devicesStraight,
1366 | manufacturersShifted, devicesShifted,
1367 | chipInfo))
1368 | {
1369 | // Use the sector info of the first valid chip we find in the info returned
1370 | foreach (ChipID::ChipInfo const &info, chipInfo)
1371 | {
1372 | if (info.capacity != 0)
1373 | {
1374 | sectorGroups = info.sectors;
1375 | break;
1376 | }
1377 | }
1378 | }
1379 |
1380 | // OK, we have the sector info saved. Now, let's do it!
1381 | if (identifyWriteIsEntireSIMM)
1382 | {
1383 | startProgrammerCommand(SetSectorLayout, WriteSIMMWaitingSetSectorLayoutReply);
1384 | }
1385 | else
1386 | {
1387 | startProgrammerCommand(SetSectorLayout, WritePortionWaitingSetSectorLayoutReply);
1388 | }
1389 | }
1390 | }
1391 | else
1392 | {
1393 | // Now we need to do the shifted version, so do another whole identification cycle
1394 | // with the other shift state
1395 | curState = IdentificationWaitingSetSizeReply;
1396 | sendByte(SetSIMMLayout_AddressShifted);
1397 | }
1398 | break;
1399 |
1400 | // WRITE BOOTLOADER PROGRAM STATE HANDLERS
1401 |
1402 | // Expecting reply after we asked to flash the firmware
1403 | case BootloaderEraseProgramAwaitingStartOKReply:
1404 | if (c == CommandReplyOK)
1405 | {
1406 | emit firmwareFlashStatusChanged(FirmwareFlashStarting);
1407 | sendByte(ComputerBootloaderWriteMore);
1408 | curState = BootloaderEraseProgramWaitingWriteMoreReply;
1409 | }
1410 | else
1411 | {
1412 | curState = WaitingForNextCommand;
1413 | closePort();
1414 | firmwareFile->close();
1415 | delete firmwareFile;
1416 | firmwareFile = NULL;
1417 | emit firmwareFlashStatusChanged(FirmwareFlashError);
1418 | }
1419 | break;
1420 |
1421 | // Expecting reply after we told bootloader we're done flashing firmware
1422 | case BootloaderEraseProgramWaitingFinishReply:
1423 | if (c == BootloaderWriteOK)
1424 | {
1425 | curState = WaitingForNextCommand;
1426 | closePort();
1427 | firmwareFile->close();
1428 | delete firmwareFile;
1429 | firmwareFile = NULL;
1430 | emit firmwareFlashStatusChanged(FirmwareFlashComplete);
1431 | }
1432 | else
1433 | {
1434 | curState = WaitingForNextCommand;
1435 | closePort();
1436 | firmwareFile->close();
1437 | delete firmwareFile;
1438 | firmwareFile = NULL;
1439 | emit firmwareFlashStatusChanged(FirmwareFlashError);
1440 | }
1441 | break;
1442 |
1443 | // Expecting reply after we asked to write more firmware data
1444 | case BootloaderEraseProgramWaitingWriteMoreReply:
1445 | if (c == BootloaderWriteOK)
1446 | {
1447 | // Send the next chunk of data
1448 | qDebug() << "Bootloader replied OK to send 1024 bytes of data! Sending...";
1449 | int chunkSize = FIRMWARE_CHUNK_SIZE;
1450 | if (firmwareLenRemaining < FIRMWARE_CHUNK_SIZE)
1451 | {
1452 | chunkSize = firmwareLenRemaining;
1453 | }
1454 |
1455 | // Read the chunk from the file!
1456 | QByteArray thisChunk = firmwareFile->read(chunkSize);
1457 |
1458 | // If it isn't FIRMWARE_CHUNK_SIZE, pad the rest with 0xFF
1459 | // (unprogrammed bytes)
1460 | for (int x = firmwareLenRemaining; x < FIRMWARE_CHUNK_SIZE; x++)
1461 | {
1462 | thisChunk.append(0xFF);
1463 | }
1464 |
1465 | // Write the chunk out (it's asynchronous so will return immediately)
1466 | serialPort->write(thisChunk);
1467 |
1468 | // OK, now we're waiting to hear back from the programmer on the result
1469 | qDebug() << "Waiting for status reply...";
1470 | curState = BootloaderEraseProgramWaitingWriteReply;
1471 | firmwareLenRemaining -= chunkSize;
1472 | firmwareLenWritten += chunkSize;
1473 | emit firmwareFlashCompletionLengthChanged(firmwareLenWritten);
1474 | }
1475 | else
1476 | {
1477 | curState = WaitingForNextCommand;
1478 | closePort();
1479 | firmwareFile->close();
1480 | delete firmwareFile;
1481 | firmwareFile = NULL;
1482 | emit firmwareFlashStatusChanged(FirmwareFlashError);
1483 | }
1484 | break;
1485 |
1486 | // Expecting reply after we sent a chunk of firmware data
1487 | case BootloaderEraseProgramWaitingWriteReply:
1488 | if (c == CommandReplyOK)
1489 | {
1490 | // Either ask to send the next chunk, or send a "finish" response
1491 | if (firmwareLenRemaining > 0)
1492 | {
1493 | sendByte(ComputerBootloaderWriteMore);
1494 | curState = BootloaderEraseProgramWaitingWriteMoreReply;
1495 | }
1496 | else
1497 | {
1498 | sendByte(ComputerBootloaderFinish);
1499 | curState = BootloaderEraseProgramWaitingFinishReply;
1500 | }
1501 | }
1502 | else
1503 | {
1504 | curState = WaitingForNextCommand;
1505 | closePort();
1506 | firmwareFile->close();
1507 | delete firmwareFile;
1508 | firmwareFile = NULL;
1509 | emit firmwareFlashStatusChanged(FirmwareFlashError);
1510 | }
1511 | break;
1512 |
1513 | // READ FIRMWARE VERSION STATE HANDLERS
1514 |
1515 | // Expecting reply after we asked for the firmware to report its version
1516 | case ReadFWVersionAwaitingOKReply:
1517 | if (c == CommandReplyOK)
1518 | {
1519 | // We should now be expecting to receive 4 bytes containing the firmware
1520 | firmwareVersionBeingAssembled = 0;
1521 | firmwareVersionNextExpectedByte = 0;
1522 | curState = ReadFWVersionWaitingData;
1523 | }
1524 | else if (c == CommandReplyInvalid)
1525 | {
1526 | // This is an older firmware not supported
1527 | curState = WaitingForNextCommand;
1528 | closePort();
1529 | emit readFirmwareVersionStatusChanged(ReadFirmwareVersionCommandNotSupported, 0);
1530 | }
1531 | else
1532 | {
1533 | // Error occurred
1534 | curState = WaitingForNextCommand;
1535 | closePort();
1536 | emit readFirmwareVersionStatusChanged(ReadFirmwareVersionError, 0);
1537 | }
1538 | break;
1539 |
1540 | // Reading the firmware version data
1541 | case ReadFWVersionWaitingData:
1542 | firmwareVersionBeingAssembled <<= 8;
1543 | firmwareVersionBeingAssembled |= c;
1544 | firmwareVersionNextExpectedByte++;
1545 | if (firmwareVersionNextExpectedByte >= 4)
1546 | {
1547 | curState = ReadFWVersionAwaitingDoneReply;
1548 | }
1549 | break;
1550 |
1551 | // Waiting for the final OK reply
1552 | case ReadFWVersionAwaitingDoneReply:
1553 | closePort();
1554 | curState = WaitingForNextCommand;
1555 | if (c == ProgrammerGetFWVersionDone)
1556 | {
1557 | emit readFirmwareVersionStatusChanged(ReadFirmwareVersionSucceeded, firmwareVersionBeingAssembled);
1558 | }
1559 | else
1560 | {
1561 | emit readFirmwareVersionStatusChanged(ReadFirmwareVersionError, 0);
1562 | }
1563 | break;
1564 |
1565 | // UNUSED STATE HANDLERS (They are handled elsewhere)
1566 | case BootloaderStateAwaitingPlug:
1567 | case BootloaderStateAwaitingUnplug:
1568 | case BootloaderStateAwaitingPlugToBootloader:
1569 | case BootloaderStateAwaitingUnplugToBootloader:
1570 | break;
1571 | }
1572 | }
1573 |
1574 | void Programmer::runElectricalTest()
1575 | {
1576 | startProgrammerCommand(DoElectricalTest, ElectricalTestWaitingStartReply);
1577 | }
1578 |
1579 | QString Programmer::electricalTestPinName(uint8_t index)
1580 | {
1581 | if (index <= LAST_ADDRESS_LINE_FAIL_INDEX)
1582 | {
1583 | return QString("A%1").arg(index - FIRST_ADDRESS_LINE_FAIL_INDEX);
1584 | }
1585 | else if (index <= LAST_DATA_LINE_FAIL_INDEX)
1586 | {
1587 | // The byte ordering is backwards to the labeling, so I have to fix that.
1588 | // Reverse the byte ordering so we have the correct number in terms of how
1589 | // D0 to D31 are labeled...
1590 | index = index - FIRST_DATA_LINE_FAIL_INDEX;
1591 | if (index < 8)
1592 | {
1593 | index = index + 24;
1594 | }
1595 | else if (index < 16)
1596 | {
1597 | index = index + 8;
1598 | }
1599 | else if (index < 24)
1600 | {
1601 | index = index - 8;
1602 | }
1603 | else
1604 | {
1605 | index = index - 24;
1606 | }
1607 | return QString("D%1").arg(index);
1608 | }
1609 | else if (index == CS_FAIL_INDEX)
1610 | {
1611 | return "CS";
1612 | }
1613 | else if (index == OE_FAIL_INDEX)
1614 | {
1615 | return "OE";
1616 | }
1617 | else if (index == WE_FAIL_INDEX)
1618 | {
1619 | return "WE";
1620 | }
1621 | else if (index == GROUND_FAIL_INDEX)
1622 | {
1623 | return "GND";
1624 | }
1625 | else if (index == VCC_FAIL_INDEX)
1626 | {
1627 | return "+5V";
1628 | }
1629 | else
1630 | {
1631 | return "?";
1632 | }
1633 | }
1634 |
1635 | void Programmer::identifySIMMChips()
1636 | {
1637 | // Start with straight addresses
1638 | identifyIsForWriteAttempt = false;
1639 | identificationShiftCounter = 0;
1640 | startProgrammerCommand(SetSIMMLayout_AddressStraight, IdentificationWaitingSetSizeReply);
1641 | }
1642 |
1643 | void Programmer::getChipIdentity(int chipIndex, uint8_t *manufacturer, uint8_t *device, bool shiftedUnlock)
1644 | {
1645 | if ((chipIndex >= 0) && (chipIndex < 4))
1646 | {
1647 | *manufacturer = chipManufacturerIDs[shiftedUnlock][chipIndex];
1648 | *device = chipDeviceIDs[shiftedUnlock][chipIndex];
1649 | }
1650 | else
1651 | {
1652 | *manufacturer = 0;
1653 | *device = 0;
1654 | }
1655 | }
1656 |
1657 | void Programmer::requestFirmwareVersion()
1658 | {
1659 | startProgrammerCommand(GetFirmwareVersion, ReadFWVersionAwaitingOKReply);
1660 | }
1661 |
1662 | void Programmer::flashFirmware(QByteArray firmware)
1663 | {
1664 | firmwareFile = new QBuffer();
1665 | firmwareFile->setData(firmware);
1666 | if (!firmwareFile->open(QFile::ReadOnly))
1667 | {
1668 | curState = WaitingForNextCommand;
1669 | emit firmwareFlashStatusChanged(FirmwareFlashError);
1670 | return;
1671 | }
1672 |
1673 | firmwareLenWritten = 0;
1674 | firmwareLenRemaining = firmwareFile->size();
1675 | emit firmwareFlashTotalLengthChanged(firmwareLenRemaining);
1676 | emit firmwareFlashCompletionLengthChanged(firmwareLenWritten);
1677 |
1678 | startBootloaderCommand(BootloaderEraseAndWriteProgram, BootloaderEraseProgramAwaitingStartOKReply);
1679 | }
1680 |
1681 | // Begins a command by opening the serial port, making sure we're in the PROGRAMMER
1682 | // rather than the bootloader, then sending a command and setting a new command state.
1683 | // TODO: When it fails, this needs to carry errors over somehow.
1684 | // newState is really just a ProgrammerCommandState but in order to keep
1685 | // ProgrammerCommandState private, I did it this way.
1686 | void Programmer::startProgrammerCommand(uint8_t commandByte, uint32_t newState)
1687 | {
1688 | nextState = (ProgrammerCommandState)newState;
1689 | nextSendByte = commandByte;
1690 |
1691 | curState = BootloaderStateAwaitingOKReply;
1692 | openPort();
1693 | sendByte(GetBootloaderState);
1694 | }
1695 |
1696 | // Begins a command by opening the serial port, making sure we're in the BOOTLOADER
1697 | // rather than the programmer, then sending a command and setting a new command state.
1698 | // TODO: When it fails, this needs to carry errors over somehow.
1699 | // newState is really just a ProgrammerCommandState but in order to keep
1700 | // ProgrammerCommandState private, I did it this way.
1701 | void Programmer::startBootloaderCommand(uint8_t commandByte, uint32_t newState)
1702 | {
1703 | nextState = (ProgrammerCommandState)newState;
1704 | nextSendByte = commandByte;
1705 |
1706 | curState = BootloaderStateAwaitingOKReplyToBootloader;
1707 | openPort();
1708 | sendByte(GetBootloaderState);
1709 | }
1710 |
1711 | void Programmer::portDiscovered(const QextPortInfo &info)
1712 | {
1713 | if ((foundState == ProgrammerBoardNotFound) &&
1714 | (info.vendorID == PROGRAMMER_USB_VENDOR_ID) &&
1715 | (info.productID == PROGRAMMER_USB_DEVICE_ID) &&
1716 | (info.portName != ""))
1717 | {
1718 | // Note: I check that portName != "" because QextSerialEnumerator seems to give me
1719 | // 2 notifications that match the vendor ID -- one is the real deal, and the other
1720 | // has a blank port name. If I match on the blank port name one, it breaks.
1721 |
1722 | #ifdef Q_WS_WIN
1723 | programmerBoardPortName = "\\\\.\\" + info.portName;
1724 | #else
1725 | programmerBoardPortName = info.portName;
1726 | #endif
1727 | foundState = ProgrammerBoardFound;
1728 | detectedDeviceRevision = info.revision;
1729 |
1730 | // I create a temporary timer here because opening it immediately seems to crash
1731 | // Mac OS X in my limited testing. Don't worry about a memory leak -- the
1732 | // portDiscovered_internal() slot will delete the newly-allocated QTimer.
1733 | QTimer *t = new QTimer();
1734 | connect(t, SIGNAL(timeout()), SLOT(portDiscovered_internal()));
1735 | t->setInterval(50);
1736 | t->setSingleShot(true);
1737 | t->start();
1738 | }
1739 | }
1740 |
1741 | void Programmer::portDiscovered_internal()
1742 | {
1743 | // Delete the QTimer that sent us this signal. Ugly, but it works...
1744 | sender()->deleteLater();
1745 |
1746 | closePort();
1747 | serialPort->setPortName(programmerBoardPortName);
1748 |
1749 | // Don't show the "control" screen if we intentionally
1750 | // reconnected the USB port because we are changing from bootloader
1751 | // to programmer mode or vice-versa.
1752 | if (curState == BootloaderStateAwaitingPlug)
1753 | {
1754 | openPort();
1755 | curState = nextState;
1756 | sendByte(nextSendByte);
1757 | }
1758 | else if (curState == BootloaderStateAwaitingPlugToBootloader)
1759 | {
1760 | openPort();
1761 | curState = nextState;
1762 | sendByte(nextSendByte);
1763 | }
1764 | else
1765 | {
1766 | emit programmerBoardConnected();
1767 | }
1768 | }
1769 |
1770 | void Programmer::portRemoved(const QextPortInfo &info)
1771 | {
1772 | const bool matchingVIDPID = info.vendorID == PROGRAMMER_USB_VENDOR_ID && info.productID == PROGRAMMER_USB_DEVICE_ID;
1773 | const bool matchingPortName = programmerBoardPortName != "" && info.portName == programmerBoardPortName;
1774 | if ((matchingVIDPID || matchingPortName) &&
1775 | (foundState == ProgrammerBoardFound))
1776 | {
1777 | programmerBoardPortName = "";
1778 | foundState = ProgrammerBoardNotFound;
1779 | detectedDeviceRevision = 0;
1780 |
1781 | // Don't show the "no programmer connected" screen if we intentionally
1782 | // disconnected the USB port because we are changing from bootloader
1783 | // to programmer mode or vice-versa.
1784 | if (curState == BootloaderStateAwaitingUnplug)
1785 | {
1786 | curState = BootloaderStateAwaitingPlug;
1787 | }
1788 | else if (curState == BootloaderStateAwaitingUnplugToBootloader)
1789 | {
1790 | curState = BootloaderStateAwaitingPlugToBootloader;
1791 | }
1792 | else
1793 | {
1794 | closePort();
1795 |
1796 | if (curState != WaitingForNextCommand)
1797 | {
1798 | // This means they unplugged while we were in the middle
1799 | // of an operation. Reset state, and let them know.
1800 | curState = WaitingForNextCommand;
1801 | emit programmerBoardDisconnectedDuringOperation();
1802 | }
1803 | else
1804 | {
1805 | emit programmerBoardDisconnected();
1806 | }
1807 | }
1808 | }
1809 | }
1810 |
1811 | void Programmer::startCheckingPorts()
1812 | {
1813 | QextSerialEnumerator *p = new QextSerialEnumerator();
1814 | connect(p, SIGNAL(deviceDiscovered(QextPortInfo)), SLOT(portDiscovered(QextPortInfo)));
1815 | connect(p, SIGNAL(deviceRemoved(QextPortInfo)), SLOT(portRemoved(QextPortInfo)));
1816 | p->setUpNotifications();
1817 | }
1818 |
1819 | void Programmer::openPort()
1820 | {
1821 | serialPort->open(QextSerialPort::ReadWrite);
1822 | }
1823 |
1824 | void Programmer::closePort()
1825 | {
1826 | serialPort->close();
1827 | }
1828 |
1829 | void Programmer::setSIMMType(uint32_t bytes, uint32_t chip_type)
1830 | {
1831 | _simmCapacity = bytes;
1832 | _simmChip = chip_type;
1833 | }
1834 |
1835 | uint32_t Programmer::SIMMCapacity() const
1836 | {
1837 | return _simmCapacity;
1838 | }
1839 |
1840 | uint32_t Programmer::SIMMChip() const
1841 | {
1842 | return _simmChip;
1843 | }
1844 |
1845 | void Programmer::setVerifyMode(VerificationOption mode)
1846 | {
1847 | _verifyMode = mode;
1848 | }
1849 |
1850 | VerificationOption Programmer::verifyMode() const
1851 | {
1852 | return _verifyMode;
1853 | }
1854 |
1855 | ProgrammerRevision Programmer::programmerRevision() const
1856 | {
1857 | return static_cast(detectedDeviceRevision);
1858 | }
1859 |
1860 | bool Programmer::selectedSIMMTypeUsesShiftedUnlock() const
1861 | {
1862 | return SIMMChip() == SIMM_TSOP_x8;
1863 | }
1864 |
1865 | void Programmer::doVerifyAfterWriteCompare()
1866 | {
1867 | // Do the comparison, emit the correct signal
1868 |
1869 | // Read the entire file we just wrote into a QByteArray
1870 | writeDevice->seek(readOffset);
1871 | QByteArray originalFileContents = writeDevice->read(verifyLength);
1872 | qDebug() << "Read" << originalFileContents.length() << "bytes, asked for" << verifyLength;
1873 |
1874 | WriteStatus emitStatus;
1875 |
1876 | // Now, compare the readback (but only for the length of originalFileContents
1877 | // (because the readback might be longer since it has to be a multiple of
1878 | // READ_CHUNK_SIZE)
1879 | if (originalFileContents.size() <= verifyArray->size())
1880 | {
1881 | const char *fileBytesPtr = originalFileContents.constData();
1882 | const char *readBytesPtr = verifyArray->constData();
1883 |
1884 | if (memcmp(fileBytesPtr, readBytesPtr, originalFileContents.size()) != 0)
1885 | {
1886 | // Now let's do some trickery and figure out which chip is acting up (or chips)
1887 | _verifyBadChipMask = 0;
1888 |
1889 | // Keep a list of which chips are reading bad data back
1890 | for (int x = 0; (x < originalFileContents.size()) && (_verifyBadChipMask != 0xF); x++)
1891 | {
1892 | if (fileBytesPtr[x] != readBytesPtr[x])
1893 | {
1894 | // OK, we found a mismatched byte. Now look at
1895 | // which byte (0-3) it is in each 4-byte group.
1896 | // If it's byte 0, it's the MOST significant byte
1897 | // because the 68k is big endian. IC4 contains the
1898 | // MSB, so IC4 is the first chip, IC3 second, and
1899 | // so on. That's why I subtract it from 3 --
1900 | // 0 through 3 get mapped to 3 through 0.
1901 | _verifyBadChipMask |= (1 << (3 - (x % 4)));
1902 | }
1903 | }
1904 |
1905 | // Now make sure we're not complaining about chips we didn't
1906 | // write to...this will zero out errors on chips we weren't
1907 | // flashing, but will leave errors intact for chips we did write.
1908 | // (the chip mask is backwards from the IC numbering...that's why
1909 | // I have to do this in a special way)
1910 | if ((writeChipMask & 0x01) == 0) _verifyBadChipMask &= ~0x08;
1911 | if ((writeChipMask & 0x02) == 0) _verifyBadChipMask &= ~0x04;
1912 | if ((writeChipMask & 0x04) == 0) _verifyBadChipMask &= ~0x02;
1913 | if ((writeChipMask & 0x08) == 0) _verifyBadChipMask &= ~0x01;
1914 | if (_verifyBadChipMask != 0)
1915 | {
1916 | emitStatus = WriteVerificationFailure;
1917 | }
1918 | else
1919 | {
1920 | emitStatus = WriteCompleteVerifyOK;
1921 | }
1922 | }
1923 | else
1924 | {
1925 | emitStatus = WriteCompleteVerifyOK;
1926 | }
1927 | }
1928 | else
1929 | {
1930 | // Wrong amount of data read back for some reason...shouldn't ever happen,
1931 | // but I'll call it a verification failure.
1932 | emitStatus = WriteVerificationFailure;
1933 | }
1934 |
1935 | // Reset verification buffer to emptiness
1936 | verifyArray->clear();
1937 | verifyBuffer->seek(0);
1938 |
1939 | // Finally, emit the final status signal
1940 | emit writeStatusChanged(emitStatus);
1941 | }
1942 |
--------------------------------------------------------------------------------
/programmer.h:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2011-2012 Doug Brown
3 | *
4 | * This program is free software; you can redistribute it and/or
5 | * modify it under the terms of the GNU General Public License
6 | * as published by the Free Software Foundation; either version 2
7 | * of the License, or (at your option) any later version.
8 | *
9 | * This program is distributed in the hope that it will be useful,
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 | * GNU General Public License for more details.
13 | *
14 | * You should have received a copy of the GNU General Public License
15 | * along with this program; if not, write to the Free Software
16 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 | *
18 | */
19 |
20 | #ifndef PROGRAMMER_H
21 | #define PROGRAMMER_H
22 |
23 | #include
24 | #include
25 | #include
26 | #include
27 | #include
28 | #include "chipid.h"
29 | #include
30 | #include
31 |
32 | typedef enum StartStatus
33 | {
34 | ProgrammerInitializing,
35 | ProgrammerInitialized
36 | } StartStatus;
37 |
38 | typedef enum ReadStatus
39 | {
40 | ReadStarting,
41 | ReadComplete,
42 | ReadError,
43 | ReadCancelled,
44 | ReadTimedOut
45 | } ReadStatus;
46 |
47 | typedef enum WriteStatus
48 | {
49 | WriteErasing,
50 | WriteCompleteNoVerify,
51 | WriteError,
52 | WriteCancelled,
53 | WriteEraseComplete,
54 | WriteEraseFailed,
55 | WriteTimedOut,
56 | WriteFileTooBig,
57 | WriteNeedsFirmwareUpdateBiggerSIMM,
58 | WriteNeedsFirmwareUpdateVerifyWhileWrite,
59 | WriteVerifying,
60 | WriteVerificationFailure,
61 | WriteVerifyStarting,
62 | WriteVerifyError,
63 | WriteVerifyCancelled,
64 | WriteVerifyTimedOut,
65 | WriteCompleteVerifyOK,
66 | WriteEraseBlockWrongSize,
67 | WriteNeedsFirmwareUpdateErasePortion,
68 | WriteNeedsFirmwareUpdateIndividualChips
69 | } WriteStatus;
70 |
71 | typedef enum ElectricalTestStatus
72 | {
73 | ElectricalTestStarted,
74 | ElectricalTestPassed,
75 | ElectricalTestFailed,
76 | ElectricalTestTimedOut,
77 | ElectricalTestCouldntStart
78 | } ElectricalTestStatus;
79 |
80 | typedef enum IdentificationStatus
81 | {
82 | IdentificationStarting,
83 | IdentificationComplete,
84 | IdentificationError,
85 | IdentificationTimedOut,
86 | IdentificationNeedsFirmwareUpdate
87 | } IdentificationStatus;
88 |
89 | typedef enum FirmwareFlashStatus
90 | {
91 | FirmwareFlashStarting,
92 | FirmwareFlashComplete,
93 | FirmwareFlashError,
94 | FirmwareFlashCancelled,
95 | FirmwareFlashTimedOut
96 | } FirmwareFlashStatus;
97 |
98 | // Various choices for verification
99 | typedef enum VerificationOption
100 | {
101 | NoVerification,
102 | VerifyWhileWriting,
103 | VerifyAfterWrite
104 | } VerificationOption;
105 |
106 | typedef enum ProgrammerRevision
107 | {
108 | ProgrammerRevisionUnknown = 0,
109 | ProgrammerRevisionAVR = 1,
110 | ProgrammerRevisionM258KE = 2
111 | } ProgrammerRevision;
112 |
113 | typedef enum ReadFirmwareVersionStatus
114 | {
115 | ReadFirmwareVersionCommandNotSupported,
116 | ReadFirmwareVersionError,
117 | ReadFirmwareVersionSucceeded
118 | } ReadFirmwareVersionStatus;
119 |
120 | // Electrical test indexes
121 | #define GROUND_FAIL_INDEX 0xFF
122 | #define VCC_FAIL_INDEX 0xFE
123 |
124 | #define FIRST_ADDRESS_LINE_FAIL_INDEX 0
125 | #define LAST_ADDRESS_LINE_FAIL_INDEX (FIRST_ADDRESS_LINE_FAIL_INDEX + 20)
126 | #define FIRST_DATA_LINE_FAIL_INDEX (LAST_ADDRESS_LINE_FAIL_INDEX + 1)
127 | #define LAST_DATA_LINE_FAIL_INDEX (FIRST_DATA_LINE_FAIL_INDEX + 31)
128 | #define CS_FAIL_INDEX (LAST_DATA_LINE_FAIL_INDEX + 1)
129 | #define OE_FAIL_INDEX (CS_FAIL_INDEX + 1)
130 | #define WE_FAIL_INDEX (OE_FAIL_INDEX + 1)
131 |
132 | #define SIMM_PLCC_x8 0x00
133 | #define SIMM_TSOP_x8 0x01
134 | #define SIMM_TSOP_x16 0x02
135 |
136 | class Programmer : public QObject
137 | {
138 | Q_OBJECT
139 | public:
140 | explicit Programmer(QObject *parent = 0);
141 | virtual ~Programmer();
142 | void readSIMM(QIODevice *device, uint32_t len = 0);
143 | void writeToSIMM(QIODevice *device, uint8_t chipsMask = 0x0F);
144 | void writeToSIMM(QIODevice *device, uint32_t startOffset, uint32_t length, uint8_t chipsMask = 0x0F);
145 | void runElectricalTest();
146 | QString electricalTestPinName(uint8_t index);
147 | void identifySIMMChips();
148 | void getChipIdentity(int chipIndex, uint8_t *manufacturer, uint8_t *device, bool shiftedUnlock);
149 | void requestFirmwareVersion();
150 | void flashFirmware(QByteArray firmware);
151 | void startCheckingPorts();
152 | void setSIMMType(uint32_t bytes, uint32_t chip_type);
153 | uint32_t SIMMCapacity() const;
154 | uint32_t SIMMChip() const;
155 | void setVerifyMode(VerificationOption mode);
156 | VerificationOption verifyMode() const;
157 | uint8_t verifyBadChipMask() const { return _verifyBadChipMask; }
158 | ProgrammerRevision programmerRevision() const;
159 | bool selectedSIMMTypeUsesShiftedUnlock() const;
160 | ChipID &chipID() { return _chipID; }
161 | signals:
162 | void startStatusChanged(StartStatus status);
163 |
164 | void readStatusChanged(ReadStatus status);
165 | void readTotalLengthChanged(uint32_t total);
166 | void readCompletionLengthChanged(uint32_t total);
167 |
168 | void writeStatusChanged(WriteStatus status);
169 | void writeTotalLengthChanged(uint32_t total);
170 | void writeCompletionLengthChanged(uint32_t len);
171 | void writeVerifyTotalLengthChanged(uint32_t total);
172 | void writeVerifyCompletionLengthChanged(uint32_t total);
173 |
174 | void electricalTestStatusChanged(ElectricalTestStatus status);
175 | void electricalTestFailLocation(uint8_t loc1, uint8_t loc2);
176 |
177 | void identificationStatusChanged(IdentificationStatus status);
178 |
179 | void firmwareFlashStatusChanged(FirmwareFlashStatus status);
180 | void firmwareFlashTotalLengthChanged(uint32_t total);
181 | void firmwareFlashCompletionLengthChanged(uint32_t total);
182 |
183 | void readFirmwareVersionStatusChanged(ReadFirmwareVersionStatus status, uint32_t version);
184 |
185 | void programmerBoardConnected();
186 | void programmerBoardDisconnected();
187 | void programmerBoardDisconnectedDuringOperation();
188 | public slots:
189 |
190 | private:
191 | //QFile *readFile;
192 | //QFile *writeFile;
193 | QIODevice *readDevice;
194 | QIODevice *writeDevice;
195 | QBuffer *firmwareFile;
196 |
197 | QextSerialPort *serialPort;
198 | void sendByte(uint8_t b);
199 | void sendWord(uint32_t w);
200 | uint8_t readByte();
201 | void handleChar(uint8_t c);
202 | uint32_t _simmCapacity;
203 | uint32_t _simmChip;
204 |
205 | uint32_t writeLenRemaining;
206 | uint32_t lenWritten;
207 | uint32_t electricalTestErrorCounter;
208 | uint8_t electricalTestFirstErrorLoc;
209 |
210 | uint32_t readChunkLenRemaining;
211 | uint32_t lenRead;
212 | uint32_t trueLenToRead;
213 | uint32_t lenRemaining;
214 | uint32_t readOffset;
215 |
216 | int identificationShiftCounter;
217 | int identificationReadCounter;
218 | uint8_t chipManufacturerIDs[2][4];
219 | uint8_t chipDeviceIDs[2][4];
220 | bool identifyIsForWriteAttempt;
221 | bool identifyWriteIsEntireSIMM;
222 | QList > sectorGroups;
223 |
224 | uint16_t detectedDeviceRevision;
225 | uint32_t firmwareLenRemaining;
226 | uint32_t firmwareLenWritten;
227 |
228 | VerificationOption _verifyMode;
229 | uint8_t _verifyBadChipMask;
230 | bool isReadVerifying;
231 | QBuffer *verifyBuffer;
232 | QByteArray *verifyArray;
233 | uint32_t verifyLength;
234 |
235 | uint32_t writeOffset;
236 | uint32_t writeLength;
237 | uint8_t writeChipMask;
238 |
239 | uint32_t firmwareVersionBeingAssembled;
240 | uint8_t firmwareVersionNextExpectedByte;
241 |
242 | ChipID _chipID;
243 |
244 | void openPort();
245 | void closePort();
246 |
247 | void internalReadSIMM(QIODevice *device, uint32_t len, uint32_t offset = 0);
248 | void startProgrammerCommand(uint8_t commandByte, uint32_t newState);
249 | void startBootloaderCommand(uint8_t commandByte, uint32_t newState);
250 | void doVerifyAfterWriteCompare();
251 |
252 | private slots:
253 | void dataReady();
254 |
255 | void portDiscovered(const QextPortInfo &info);
256 | void portDiscovered_internal();
257 | void portRemoved(const QextPortInfo &info);
258 | };
259 |
260 | #endif // PROGRAMMER_H
261 |
--------------------------------------------------------------------------------
/textbrowserwithlinks.cpp:
--------------------------------------------------------------------------------
1 | #include "textbrowserwithlinks.h"
2 | #include
3 |
4 | TextBrowserWithLinks::TextBrowserWithLinks(QWidget *parent) :
5 | QTextBrowser(parent)
6 | {
7 |
8 | }
9 |
10 | void TextBrowserWithLinks::setHtml(const QString &text)
11 | {
12 | _originalHtml = text;
13 | QTextBrowser::setHtml(text);
14 | }
15 |
16 | void TextBrowserWithLinks::changeEvent(QEvent *event)
17 | {
18 | if (event->type() == QEvent::PaletteChange)
19 | {
20 | QTextBrowser::setText(_originalHtml);
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/textbrowserwithlinks.h:
--------------------------------------------------------------------------------
1 | #ifndef TEXTBROWSERWITHLINKS_H
2 | #define TEXTBROWSERWITHLINKS_H
3 |
4 | #include
5 |
6 | class TextBrowserWithLinks : public QTextBrowser
7 | {
8 | Q_OBJECT
9 | public:
10 | explicit TextBrowserWithLinks(QWidget *parent = NULL);
11 | void setHtml(QString const &text);
12 |
13 | protected:
14 | virtual void changeEvent(QEvent *event);
15 |
16 | private:
17 | QString _originalHtml;
18 | };
19 |
20 | #endif // TEXTBROWSERWITHLINKS_H
21 |
--------------------------------------------------------------------------------