├── .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 |
labelwithlinks.h
144 |
145 | 146 | TextBrowserWithLinks 147 | QTextBrowser 148 |
textbrowserwithlinks.h
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 | 820 | 821 | 822 | 0 823 | 0 824 | 500 825 | 21 826 | 827 | 828 | 829 | 830 | File 831 | 832 | 833 | 834 | 835 | 836 | 837 | Advanced 838 | 839 | 840 | 841 | 842 | 843 | 844 | 845 | 846 | Help 847 | 848 | 849 | 850 | 851 | 852 | 853 | 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 |
droppablegroupbox.h
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 | --------------------------------------------------------------------------------