├── source ├── linkfile ├── linkfile-doc ├── Envious.png ├── ZX_Eurostile.png ├── sdsc.inc ├── makefile └── zexall.sms.asm ├── .gitignore ├── .github └── workflows │ └── build.yml ├── README.md └── LICENSE /source/linkfile: -------------------------------------------------------------------------------- 1 | [objects] 2 | zexall.sms.o -------------------------------------------------------------------------------- /source/linkfile-doc: -------------------------------------------------------------------------------- 1 | [objects] 2 | zexdoc.sms.o -------------------------------------------------------------------------------- /source/Envious.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-zhao/zexall-sms/HEAD/source/Envious.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.sms 2 | *.sym 3 | *.zip 4 | *.sav 5 | /tools/ 6 | *.o 7 | *.1bpp 8 | *.makefile 9 | -------------------------------------------------------------------------------- /source/ZX_Eurostile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/maxim-zhao/zexall-sms/HEAD/source/ZX_Eurostile.png -------------------------------------------------------------------------------- /.github/workflows/build.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | name: build 8 | runs-on: windows-latest 9 | 10 | steps: 11 | - name: checkout 12 | uses: actions/checkout@v2 13 | 14 | - name: get tools 15 | shell: pwsh 16 | run: | 17 | Invoke-WebRequest -Uri https://github.com/maxim-zhao/sms-build-tools/releases/download/1.0.382/tools.7z -OutFile tools.7z 18 | 7z x tools.7z -otools 19 | 20 | - name: build 21 | shell: cmd 22 | working-directory: source 23 | run: | 24 | path ..\tools;%path% 25 | make all 26 | 27 | - uses: actions/upload-artifact@v4 28 | with: 29 | name: roms 30 | path: | 31 | source\zexall.zip 32 | 33 | -------------------------------------------------------------------------------- /source/sdsc.inc: -------------------------------------------------------------------------------- 1 | ; Eric R. Quinn 2 | ; May 6, 2002 3 | ; sdsc.inc 4 | ;1234567890123456789012345678901234567890123456789012345678901234567890123456789 5 | ; 6 | ; Version 0.00: May 6, 2002 7 | ; ------------------------- 8 | ; 9 | ; + Initial version 10 | ; 11 | 12 | 13 | ; "OFFSETS" are relative to the beginning of a ROM page or a Page Frame 14 | ; 15 | .DEFINE SDSC_OFFSET_SDSC_TAG $3FE0 16 | 17 | 18 | ; I/O Port Definitions 19 | ; 20 | .DEFINE SDSC_OUTPORT_DEBUGCONSOLE_COMMAND $0FC 21 | .DEFINE SDSC_OUTPORT_DEBUGCONSOLE_DATA $0FD 22 | 23 | 24 | .DEFINE SDSC_DEBUGCONSOLE_COMMAND_SUSPENDEMULATION $01 25 | .DEFINE SDSC_DEBUGCONSOLE_COMMAND_CLEARSCREEN $02 26 | .DEFINE SDSC_DEBUGCONSOLE_COMMAND_SETATTRIBUTE $03 27 | .DEFINE SDSC_DEBUGCONSOLE_COMMAND_MOVECURSOR $04 28 | 29 | ; END sdsc.inc -------------------------------------------------------------------------------- /source/makefile: -------------------------------------------------------------------------------- 1 | WLA-Z80 = wla-z80 2 | WLALINK = wlalink 3 | BMP2TILE = bmp2tile 4 | GENERATED_FILES = *.sms *.o *.makefile *.1bpp *.sym *.zip 5 | 7Z = 7z 6 | 7 | all: zexall.zip 8 | 9 | # We get WLA DX to generate the dependencies list for our source 10 | zexall.makefile: zexall.sms.asm 11 | $(WLA-Z80) -t -M -D UndocumentedFlags -o zexall.sms.o $^ > $@ 12 | 13 | zexdoc.makefile: zexall.sms.asm 14 | $(WLA-Z80) -t -M -o zexdoc.sms.o $^ > $@ 15 | 16 | include zexall.makefile 17 | include zexdoc.makefile 18 | 19 | # Graphics conversion 20 | %.1bpp: %.png 21 | bmp2tile "$<" -noremoveduplicates -savetiles "$@" 22 | 23 | zexall.sms.o: zexall.sms.asm 24 | $(WLA-Z80) -D UndocumentedFlags -o $@ $< 25 | 26 | zexall.sms: zexall.sms.o linkfile 27 | $(WLALINK) -d -r -v -S -A linkfile $@ 28 | 29 | zexdoc.sms.o: 30 | $(WLA-Z80) -o $@ $< 31 | 32 | zexdoc.sms: zexdoc.sms.o linkfile-doc 33 | $(WLALINK) -d -r -v -S -A linkfile-doc $@ 34 | 35 | zexall.zip: zexall.sms zexdoc.sms ..\README.md ..\LICENSE 36 | 7z a $@ $^ 37 | 7z a $@ ../source $(addprefix -xr!, $(GENERATED_FILES)) 38 | 39 | clean: 40 | del $(GENERATED_FILES) -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | SMS Z80 instruction exerciser 2 | ============================= 3 | 4 | This is a port of ZEXALL, the Z80 instruction exerciser, to the Sega 5 | Master System. It was written by Frank Cringle to go with his YAZE 6 | emulator. It was ported to CP/M and Spectrum by J.G.Harston, and 7 | subsequently to the SMS by Brett K. Unfortunately, his port didn't work 8 | on a real system due to some mistakes in the SMS-specific parts and 9 | (for some reason) produces different CRCs than were stored in the 10 | Spectrum version, although the SMS does have a regular Z80 CPU. 11 | 12 | So, I converted Brett K's source to compile with WLA DX (the best macro 13 | assembler) and fixed the errors. The console emulation code he wrote is 14 | excellent, by the way. I also masked out the undocumented flags from the 15 | f register since no SMS games seem to depend on them and if an emulator 16 | doesn't implement them it would otherwise fail almost every test. Then I 17 | ran it on a real SMS and noted the results. 18 | 19 | I also changed the ordering of the tests so that the quickest are 20 | completed first. A few of the tests are a LOT longer than others. The 21 | shortest complete in a fraction of a second and the longest takes (I 22 | think) well over an hour. You have been warned. 23 | 24 | Since ZEXALL is covered by the GNU, I am including the source and 25 | licence. 26 | 27 | Maxim 28 | 29 | ============================= 30 | 31 | June 12, 2003 32 | 33 | Eric R. Quinn 34 | 35 | Added conditional assembly direction enabling test output to go to 36 | SDSC Debug Console instead of SMS VDP. The motivation for doing this 37 | was to eliminate the need for having a functioning VDP in a SMS emulator 38 | before running ZEXALL. (Hopefully, the SDSC Debug Console is easier to 39 | implement than a VDP.) 40 | 41 | The binary zexall.sms uses the SMS VDP. 42 | 43 | The binary zexall_sdsc.sms uses the SDSC Debug Console. 44 | 45 | ============================= 46 | 47 | 2021/10/16 48 | 49 | Version 0.17 50 | 51 | This version supports various options for output: it will emit to the SMS 52 | VDP in mode 4 if detected, else it will try a TMS9918 VDP in mode 2 (text 53 | mode), so this should work on an SG-1000 or SC-3000. It will also emit 54 | simultaneously to the SDSC Debug Console and also write ASCII text to SRAM. 55 | 56 | Press Up to force mode 4, and Down to force text mode. 57 | 58 | zexall.sms includes the undocumented bits in the flags register, zexdoc.sms 59 | ignores them. 60 | 61 | You can build your own version if you want some different combination of 62 | settings. 63 | 64 | Fonts used are https://damieng.com/typography/zx-origins/envious for mode 2 65 | and https://damieng.com/typography/zx-origins/zx-eurostile for mode 4. 66 | 67 | ============================= 68 | 69 | 2025/06/16 70 | 71 | Version 0.20 72 | 73 | This version solves a couple of long-standing issues caused by inconsistency 74 | between Z80 CPUs, specifically the undocumented flags' state after the `scf` 75 | and `ccf` opcodes. All tests should now pass on "normal" Z80s seen in Sega 76 | 8-bit and 16-bit systems. 77 | 78 | Because this was achieved by ignoring these flags in this scenario, an 79 | additional pre-test was added which checks the flags after each of `scf` and 80 | `ccf`, with different preconditions regarding the contents of register `a` 81 | and the last ALU operation's flags result. Each is run $1fff times, and the 82 | screen shows the count of how many times each flag was set; it is expected 83 | that: 84 | 85 | 1. The numbers are usually all either 0 or 1fff - where any other value is seen, it 86 | indicates that the flag value is not stable. This has been observed on 87 | Sega Mark III (with a Zilog CPU) and Sega Genesis 2 (with a Sega 315-5676 88 | Z80 clone, probably made by NEC). 89 | 2. The pattern of which are 0 and which are 1fff is stable for a given CPU 90 | type, but varies by CPU type. At least four patterns are known for Sega 91 | systems. 92 | 93 | Finally, the CRC computing algorithm was improved so the tests run a bit faster. 94 | 95 | ============================= 96 | 97 | 2025/06/16 98 | 99 | Version 0.21 100 | 101 | This version adds support for the Emulicious/BGB debug console, which is 102 | enabled by default as it is harmless when not implemented. 103 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | GNU GENERAL PUBLIC LICENSE 2 | Version 2, June 1991 3 | 4 | Copyright (C) 1989, 1991 Free Software Foundation, Inc. 5 | 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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 | U0C 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 | U0C 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 | U0C 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 | U0C 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 | U0C 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) 19yy 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 307 | along with this program; if not, write to the Free Software 308 | Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 309 | 310 | 311 | Also add information on how to contact you by electronic and paper mail. 312 | 313 | If the program is interactive, make it output a short notice like this 314 | when it starts in an interactive mode: 315 | 316 | Gnomovision version 69, Copyright (C) 19yy name of author 317 | Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. 318 | This is free software, and you are welcome to redistribute it 319 | under certain conditions; type `show c' for details. 320 | 321 | The hypothetical commands `show w' and `show c' should show the appropriate 322 | parts of the General Public License. Of course, the commands you use may 323 | be called something other than `show w' and `show c'; they could even be 324 | mouse-clicks or menu items--whatever suits your program. 325 | 326 | You should also get your employer (if you work as a programmer) or your 327 | school, if any, to sign a "copyright disclaimer" for the program, if 328 | necessary. Here is a sample; alter the names: 329 | 330 | Yoyodyne, Inc., hereby disclaims all copyright interest in the program 331 | `Gnomovision' (which makes passes at compilers) written by James Hacker. 332 | 333 | , 1 April 1989 334 | Ty Coon, President of Vice 335 | 336 | This General Public License does not permit incorporating your program into 337 | proprietary programs. If your program is a subroutine library, you may 338 | consider it more useful to permit linking proprietary applications with the 339 | library. If this is what you want to do, use the GNU Library General 340 | Public License instead of this License. 341 | 342 | -------------------------------------------------------------------------------- /source/zexall.sms.asm: -------------------------------------------------------------------------------- 1 | ;.redefine UndocumentedFlags ; if defined then undocumented flags get checked too. Makefile may define this. 2 | .define WriteToSRAM ; if defined then text is emitted to SRAM 3 | .define WriteToScreen ; if defined then text is emitted to the screen 4 | .define WriteToSDSCDebugConsole ; if defined then text is emitted to the SDSC Debug Console 5 | .define WriteToEmuliciousDebugConsole ; if defined then text is emitted to the Emulicious/BGB Debug Console 6 | 7 | ; zexall.asm - Z80 instruction set exerciser 8 | ; Copyright (C) 1994 Frank D. Cringle 9 | ; 10 | ; 2025 (Maxim) 0.21 11 | ; + Added support for BGB/Emulicious debug console 12 | ; 2025 (Maxim) 0.20 13 | ; + Added a progress counter for each test 14 | ; + Fixed daa test which covered other opcodes unnecessarily 15 | ; + Fixed scf/ccf test to always ignore undocumented flags, as they are not consistent between 16 | ; Z80 CPUs. 17 | ; + Added pre-test checking which undocumented flags are set by scf/ccf in different scenarios. 18 | ; This is informational only, there is no pass/fail condition. Values other than 0000 and 1fff 19 | ; indicate inconsistency/instability in the results. 20 | ; + Added optimised CRC code from z80test, making it a bit faster again. 21 | ; 2024 (Maxim) 0.19 22 | ; + Fixed build with newer WLA DX 23 | ; + Fixed build with screen output disabled 24 | ; 2021 (Maxim) 25 | ; + Fixed slot detection code 26 | ; + Added TMS9918a compatible mode. The unmodified ROM should now run on an SG-1000 or SC-3000. 27 | ; - Press Up on a Master System at startup to force mode 4 (SMS graphics); press Down 28 | ; to force mode 2 (TMS9918 text mode); else mode 4 detection is used 29 | ; + Updated to build with recent WLA DX 30 | ; + Added optimised CRC code from asynchronous. Now it is 29% faster! 31 | ; 2016 (Maxim) 32 | ; + Tidied up source code in various unimportant ways 33 | ; + Documented all tests explicitly, including case counts 34 | ; + Amended several tests which were mistakenly testing the wrong things 35 | ; + Amended tests to iterate across the undocumented flags in undocumented flags mode 36 | ; - this affects the test case counts, ordering and comments still reflect the 37 | ; documented-only case 38 | ; + Added SRAM output option 39 | ; + Removed VBlank timing dependency 40 | ; + All output options may happen at once now 41 | ; 42 | ; 13-November-2010: (FluBBa) 43 | ; + Fixed compilation when UseSDSCDebugConsole is set to 1 44 | ; + Added correct CRCs for testing ALL flags (including the undocumented ones). 45 | ; 46 | ; 03-May-2007: (Rene S. Dare) 47 | ; + Added two moving slashes to animate the screen when Zexall is under internal processing. :) 48 | ; 49 | ; 09-February-2006: (Eric R. Quinn) 50 | ; + Fixed tests alu8x, cpd1, ld8ix1, id8ix2, ld8ix3, st8ix1, st8ix2, and st8ix3, that 51 | ; all accessed addresses outside of the MachineStateBeforeTest region. This caused 52 | ; a state leak across the load and store tests. Also, it should be noted that 53 | ; the test coverage of these tests is lacking. These tests do not test that 54 | ; negative displacements work correctly. There doesn't seem to be an easy way 55 | ; to test negative displacements (given the count/shift method used) without 56 | ; creating separate tests. 57 | ; + Fixed VDP initialization to be compatible with SMS1 VDP (315-5124) 58 | ; + Changed USE_SDSC_DEBUG_CONSOLE to UseSDSCDebugConsole to better match existing 59 | ; code style 60 | ; 61 | ; 11-June-2003: (Eric R. Quinn) 62 | ; + Modified to use SDSC Debug Console (instead of SMS VDP) for output 63 | ; + Define USE_SDSC_DEBUG_CONSOLE to 1 to use that console, otherwise uses VDP 64 | ; 65 | ; 18-Mar-2003: Tweaked a bit (Maxim) 66 | ; + Moved screen right by 8 to make it show on a real TV better 67 | ; + Added proper Pause button handler 68 | ; + Source clean-ups 69 | ; 70 | ; 13-Mar-2003: Modified to assemble with WLA DX (Maxim) 71 | ; + Banking system setup 72 | ; + 0nnh and 0nnnnh to $nn and $nnnn 73 | ; + db to .db 74 | ; + xxx equ yyy to .define xxx yyy 75 | ; + Macros redefined 76 | ; + Fixed some things that were wrong for the SMS side 77 | ; 78 | ; 28-Dec-2002: Ported to the Sega Master System (Brett K): 79 | ; + moved test code into RAM 80 | ; + moved various data locations into RAM 81 | ; + added SMS initialization routines, a character set, etc. 82 | ; + wrote print character routine to emulate console. 83 | ; 84 | ; 03-Nov-2002: Modified to assemble with ZMAC and MAXAM 85 | ; Copyright (C) 2002 J.G.Harston 86 | ; + labels on equates mustn't have trailing colon 87 | ; + macros don't understand <...> sequence, so parameters are passed 88 | ; explicitly 89 | ; + ds n, c not supported, so strings are set to full explicity length 90 | ; + nonstandard 'cp a, ' and 'and a, ' changed to 'cp ' and 'and ' 91 | ; 92 | ; 03-Nov-2002: Modified to run on Spectrum 93 | ; Copyright (C) 2002 J.G.Harston 94 | ; 95 | ; This program is free software; you can redistribute it and/or 96 | ; modify it under the terms of the GNU General Public License 97 | ; as published by the Free Software Foundation; either version 2 98 | ; of the License, or (at your option) any later version. 99 | ; 100 | ; This program is distributed in the hope that it will be useful, 101 | ; but WITHOUT ANY WARRANTY; without even the implied warranty of 102 | ; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 103 | ; GNU General Public License for more details. 104 | ; 105 | ; You should have received a copy of the GNU General Public License 106 | ; along with this program; if not, write to the Free Software 107 | ; Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 108 | 109 | 110 | ; WLA-DX banking setup 111 | .memorymap 112 | defaultslot 0 113 | slot 0 $0000 $4000 ; ROM 114 | slot 1 $4000 $4000 ; ROM (only used for headers) 115 | slot 2 $8000 $4000 ; ROM (not used) 116 | slot 3 $c000 $0400 ; RAM (1KB for SG-1000 compatibility) 117 | .endme 118 | 119 | ; We produce a 64KB ROM in order to make some emulators enable SRAM. Almost all is unused. 120 | .rombankmap 121 | bankstotal 4 122 | banksize $4000 123 | banks 4 124 | .endro 125 | 126 | ; Structs 127 | 128 | .struct MachineState 129 | memop dw ; This is always the target of any memory operation 130 | iy dw 131 | ix dw 132 | hl dw 133 | de dw 134 | bc dw 135 | f db 136 | a db 137 | sp dw 138 | .endst 139 | 140 | .define OpcodeCount 4 141 | .struct TestCase 142 | Opcode1 db 143 | Opcode2 db 144 | Opcode3 db 145 | Opcode4 db 146 | MachineState instanceof MachineState 147 | .endst 148 | 149 | .struct Test 150 | BaseState instanceof TestCase 151 | CounterBits instanceof TestCase 152 | ShifterBits instanceof TestCase 153 | ExpectedCRC dd 154 | FlagsMask db 155 | Name db ; actually a string of unknown length 156 | .endst 157 | 158 | .struct Permuter 159 | Bit db 160 | Byte dw 161 | Buffer dsw _sizeof_TestCase 162 | .endst 163 | 164 | .ramsection "Variables" slot 3 165 | Port3EValue db 166 | PrintOnlyToScreen db 167 | FlagsMaskForTest db 168 | EmuliciousConsoleChar db 169 | Counter instanceof Permuter 170 | Shifter instanceof Permuter 171 | StackPointerSaved dw ; Saved sp 172 | CRCValue dsb 4 ; CRC value 173 | MachineStateAfterTest instanceof MachineState 174 | ; CRCs are dependent on the location of this so it needs to stay at $c070. 175 | MachineStateBeforeTest instanceof MachineState 176 | PauseFlag db 177 | IsSMSVDP db 178 | TMSCopyBuffer dsb 40 179 | sccfCounters dsw 24 ; count for each bit 180 | TestInRAM dsb 256 ; WLA DX doesn't (?) have a way to make this auto-sized. 181 | .ends 182 | 183 | .ifdef UndocumentedFlags 184 | .define FlagMask %11111111 ; Mask for flag register 185 | .else 186 | .define FlagMask %11010111 ; Mask for flag register 187 | .endif 188 | ; SZXHXPNC 189 | ; |||||||`- Carry 190 | ; ||||||`-- Add/subtract 191 | ; |||||`--- Parity/overflow 192 | ; ||||`---- Undocumented 193 | ; |||`----- Half carry 194 | ; ||`------ Undocumented 195 | ; |`------- Zero 196 | ; `-------- Sign 197 | .print "FlagMask = ", HEX FlagMask, "\n" 198 | 199 | 200 | .bank 0 Slot 0 201 | 202 | .org $0000 203 | .section "Boot section" Force 204 | di 205 | im 1 206 | ld sp, $c3f0 ; This is near the top of RAM for an SG-1000. We don't need much stack. 207 | jp Start 208 | .ends 209 | 210 | .org $0066 211 | .section "Pause handler" Force 212 | ; Note that the test code sets the stack to various locations 213 | ; while testing, which may mean that pausing will break the 214 | ; program flow. It's therefore best avoided. 215 | push af 216 | ld a, (PauseFlag) 217 | xor 1 ; toggle flag 218 | ld (PauseFlag), a 219 | -:ld a, (PauseFlag) 220 | or a ; Loop if non-zero 221 | jr nz, - 222 | pop af 223 | retn 224 | .ends 225 | 226 | /* 227 | For the purposes of this test program, the machine state consists of: 228 | * a 2 byte memory operand, followed by 229 | * the registers iy, ix, hl, de, bc, af, sp 230 | for a total of 16 bytes. 231 | 232 | The program tests instructions (or groups of similar instructions) 233 | by cycling through a sequence of machine states, executing the test 234 | instruction for each one and running a 32-bit crc over the resulting 235 | machine states. At the end of the sequence the crc is compared to 236 | an expected value that was found empirically on a real Z80. 237 | 238 | A test case is defined by a descriptor which consists of: 239 | * the base case, 240 | * the increment vector, 241 | * the shift vector, 242 | * the expected crc(s), 243 | * a short descriptive message. 244 | 245 | The first three parts of the descriptor are vectors corresponding to 246 | a 4 byte instruction (padded with nops) and a multi-byte machine state. 247 | * The first part is the base case, which is the first test case of 248 | the sequence. This base is then modified according to the next two 249 | vectors. 250 | * Each 1 bit in the increment vector specifies a bit to be 251 | cycled in the form of a binary counter. For instance, if the byte 252 | corresponding to the accumulator is set to $ff in the increment 253 | vector, the test will be repeated for all 256 values of the 254 | accumulator. Note that 1 bits don't have to be contiguous. The 255 | number of test cases 'caused' by the increment vector is equal to 256 | 2^(number of 1 bits). 257 | * The shift vector is similar, but specifies a set of bits in the test 258 | case that are to be successively inverted. Thus the shift vector 259 | 'causes' a number of test cases equal to the number of 1 bits in it, 260 | plus 1 (with all bits at their initial values). 261 | 262 | The total number of test cases is the product of those caused by the 263 | counter and shift vectors and can easily become unweildy. Each 264 | individual test case can take a few milliseconds to execute, due to 265 | the overhead of test setup and crc calculation, so test design is a 266 | compromise between coverage and execution time. 267 | 268 | This program is designed to detect differences between 269 | implementations and is not ideal for diagnosing the causes of any 270 | discrepancies. However, provided a reference implementation (or 271 | real system) is available, a failing test case can be isolated by 272 | hand using a binary search of the test space. 273 | */ 274 | 275 | .section "Start" free 276 | Start: 277 | xor a 278 | ld (PauseFlag), a 279 | ld (PrintOnlyToScreen),a 280 | 281 | ; Output initialisation 282 | .ifdef WriteToScreen 283 | call Initialise_Screen 284 | .endif 285 | ; We disable inputs for SDSC console use so we have to do that after the screen check as it uses them for mode overrides 286 | .ifdef WriteToSDSCDebugConsole 287 | call Initialise_SDSC 288 | .endif 289 | .ifdef WriteToSRAM 290 | call InitialiseSRAM 291 | .endif 292 | 293 | ; Print the version 294 | ; We format it to the screen... 295 | ld ix, $7fe0 ; SDSC header location 296 | ; First the title 297 | ; Get the pointer 298 | ld l, (ix+12) 299 | ld h, (ix+13) 300 | -:ld a, (hl) 301 | or a 302 | jr z, + ; It's null-terminated 303 | call PrintChar 304 | inc hl 305 | jr - 306 | +: 307 | 308 | ; Add a space 309 | ld a, ' ' 310 | call PrintChar 311 | 312 | ; Then the major version... we assume a single digit 313 | ld a, (ix+4) 314 | call PrintNibble 315 | ld a, '.' 316 | call PrintChar 317 | ; And the minor... with two digits 318 | ld a, (ix+5) 319 | call PrintByte 320 | ld a, NEWLINE 321 | call PrintChar 322 | 323 | ld de, Message_TitleInfo 324 | call OutputText 325 | 326 | .ifdef WriteToSDSCDebugConsole 327 | ld de, Message_SDSCMode 328 | call OutputText 329 | .endif 330 | 331 | .ifdef WriteToEmuliciousDebugConsole 332 | ld de, Message_EmuliciousMode 333 | call OutputText 334 | .endif 335 | 336 | .ifdef WriteToScreen 337 | ld de, Message_SMSMode 338 | ld a, (IsSMSVDP) 339 | or a 340 | jr nz, + 341 | ld de, Message_TMSMode 342 | +:call OutputText 343 | .endif 344 | 345 | .ifdef WriteToSRAM 346 | ld de, Message_SRAMMode 347 | call OutputText 348 | .endif 349 | 350 | ld a, NEWLINE 351 | call PrintChar 352 | 353 | call sccfTest 354 | 355 | ; Copy test code to RAM 356 | ld de, TestInRAM 357 | ld hl, TestCode 358 | ld bc, _sizeof_TestCode 359 | ldir 360 | 361 | ; Run tests, stop when first word of test data is 0 362 | ld hl, Tests 363 | -:ld a, (hl) 364 | inc hl 365 | or (hl) 366 | jr z, + 367 | dec hl 368 | call StartTest ; otherwise do test 369 | jp - 370 | 371 | +:ld de, Message_Done 372 | call OutputText 373 | 374 | .ifdef WriteToSDSCDebugConsole 375 | ; Suspend emulation 376 | ld a, SDSC_DEBUGCONSOLE_COMMAND_SUSPENDEMULATION 377 | out (SDSC_OUTPORT_DEBUGCONSOLE_COMMAND), a 378 | .endif 379 | 380 | Done: 381 | -:jp - ; Infinite loop to stop program 382 | 383 | 384 | sccfTest: 385 | ; SCF/CCF test! 386 | .macro Q0_F0_A0 387 | xor a ; A = 0; YF, XF, YQ, XQ = 0 388 | .endm 389 | 390 | .macro Q0_F1_A0 391 | xor a ; 392 | dec a ; YF, XF = 1 393 | ld a, 0 ; A = 0; Q = 0 394 | .endm 395 | 396 | .macro Q1_F1_A0 397 | xor a ; A = 0 398 | ld e, a ; 399 | dec e ; YF, XF, YQ, XQ = 1 400 | .endm 401 | 402 | .macro Q0_F0_A1 403 | xor a ; YF, XF = 0 404 | ld a, $FF ; A = FFh; Q = 0 405 | .endm 406 | 407 | .macro Q0_F1_A1 408 | xor a ; 409 | dec a ; A = FFh; YF, XF = 1 410 | nop ; Q = 0 411 | .endm 412 | 413 | .macro Q1_F1_A1 414 | xor a ; 415 | dec a ; A = FFh; YF, XF, YQ, XQ = 1 416 | .endm 417 | 418 | ld de, Message_SCF_CCF 419 | call OutputText 420 | 421 | ld hl, sccfCounters 422 | ld de, sccfCounters+1 423 | ld (hl),0 424 | ld bc, _sizeof_sccfCounters 425 | ldir 426 | 427 | ld bc, $1fff ; Number of rounds to run 428 | 429 | -:ld hl, sccfCounters ; Set BC to the address of the results array. 430 | 431 | ; Test all factor combinations with `ccf` and 432 | ; keep the resulting values of YF and XF. 433 | Q0_F0_A0 434 | ccf 435 | call keep_yxf 436 | Q0_F1_A0 437 | ccf 438 | call keep_yxf 439 | Q1_F1_A0 440 | ccf 441 | call keep_yxf 442 | Q0_F0_A1 443 | ccf 444 | call keep_yxf 445 | Q0_F1_A1 446 | ccf 447 | call keep_yxf 448 | Q1_F1_A1 449 | ccf 450 | call keep_yxf 451 | 452 | ; Test all factor combinations with `scf` and 453 | ; keep the resulting values of YF and XF. 454 | Q0_F0_A0 455 | scf 456 | call keep_yxf 457 | Q0_F1_A0 458 | scf 459 | call keep_yxf 460 | Q1_F1_A0 461 | scf 462 | call keep_yxf 463 | Q0_F0_A1 464 | scf 465 | call keep_yxf 466 | Q0_F1_A1 467 | scf 468 | call keep_yxf 469 | Q1_F1_A1 470 | scf 471 | call keep_yxf 472 | 473 | dec bc 474 | ld a, b 475 | or c 476 | jr nz, - 477 | 478 | ld hl, sccfCounters+1 479 | ld c, 4 480 | --: 481 | ld b, 6 482 | -:push bc 483 | ld a,(hl) 484 | call PrintByte 485 | dec hl 486 | ld a,(hl) 487 | call PrintByte 488 | inc hl 489 | inc hl 490 | inc hl 491 | ld a, ' ' 492 | call PrintChar 493 | pop bc 494 | djnz - 495 | push bc 496 | ld a, NEWLINE 497 | call PrintChar 498 | pop bc 499 | dec c 500 | jr nz, -- 501 | 502 | keep_yxf: 503 | ; Get AF in DE 504 | push af 505 | pop de 506 | ; Check XF in the result 507 | ld a, e 508 | and %00100000 509 | call + 510 | ; And now YF 511 | ld a, e 512 | and %00001000 513 | +:; Increment word at hl if non-zero, else leave it alone. Also hl+=2 514 | jr z, ++ ; if zero, just inc and ret 515 | inc (hl) ; else increment low... 516 | inc hl 517 | jr nz, + 518 | inc (hl) 519 | jr + 520 | ++: 521 | inc hl 522 | +:inc hl 523 | ret 524 | 525 | .ends 526 | 527 | .section "Test table" free 528 | ; Lookup table of test data 529 | ; Sorted by case count, so fastest go first 530 | Tests: 531 | .ifdef UndocumentedFlags 532 | .dw ld162, ld163, ld166, ld167, ld8imx, ld161, ld164, ld16ix, ld8bd, lda, ldd1, ldd2, ldi1, ldi2, ld165, ld168, ld16im, ld8im, st8ix3, cplop, ld8ix3, stabd, rotxy, sccf, srzx, ld8ix2, st8ix2, ld8ixy, ld8ix1, st8ix1, incbc, incde, inchl, incix, inciy, incsp, bitx, ld8rr, inca, incb, incc, incd, ince, inch, incl, incm, incxh, incxl, incyh, incyl, rotz80, ld8rrx, srz80, rldop, incx, rot8080, alu8r_a, cpd1, cpi1, alu8i, alu8r_b, alu8r_c, alu8r_d, alu8r_e, alu8r_h, alu8r_l, alu8r_hl, alu8rx_ixh, alu8rx_ixl, alu8rx_iyh, alu8rx_iyl, add16, add16x, add16y, bitz80, negop, daaop, adc16, alu8x 533 | .else 534 | .dw ld162, ld163, ld166, ld167, ld8imx, ld161, ld164, ld16ix, ld8bd, lda, ldd1, ldd2, ldi1, ldi2, ld165, ld168, ld16im, ld8im, sccf, st8ix3, cplop, ld8ix3, stabd, rotxy, srzx, ld8ix2, st8ix2, ld8ixy, ld8ix1, incbc, incde, inchl, incix, inciy, incsp, st8ix1, bitx, ld8rr, inca, incb, incc, incd, ince, inch, incl, incm, incxh, incxl, incyh, incyl, rotz80, ld8rrx, srz80, incx, rot8080, rldop, alu8r_a, cpd1, cpi1, negop, daaop, alu8i, alu8r_b, alu8r_c, alu8r_d, alu8r_e, alu8r_h, alu8r_l, alu8r_hl, alu8rx_ixh, alu8rx_ixl, alu8rx_iyh, alu8rx_iyl, add16, add16x, add16y, bitz80, adc16, alu8x 535 | .endif 536 | .dw 0 537 | .ends 538 | 539 | .section "Test cases" free 540 | ; Macros: 541 | ; TestData for defining test data 542 | .macro TestData 543 | .if NARGS != 13 544 | .printt "missing parameter\n" 545 | .fail 546 | .endif 547 | .dstruct Test\@ instanceof TestCase data \1, \2, \3, \4, \5, \6, \7, \8, \9, \10, \11, \12, \13 548 | .endm 549 | .macro TestData3 550 | ; 3-byte opcode 551 | .if NARGS != 12 552 | .printt "missing parameter\n" 553 | .fail 554 | .endif 555 | TestData \1, \2, \3, 0, \4, \5, \6, \7, \8, \9, \10, \11, \12 556 | .endm 557 | .macro TestData2 558 | ; 2-byte opcode 559 | .if NARGS != 11 560 | .printt "missing parameter\n" 561 | .fail 562 | .endif 563 | TestData \1, \2, 0, 0, \3, \4, \5, \6, \7, \8, \9, \10, \11 564 | .endm 565 | .macro TestData1 566 | ; 1-byte opcode 567 | .if NARGS != 10 568 | .printt "missing parameter\n" 569 | .fail 570 | .endif 571 | TestData \1, 0, 0, 0, \2, \3, \4, \5, \6, \7, \8, \9, \10 572 | .endm 573 | .macro TestData3_16 574 | ; 3-byte opcode with 16-bit arg in last two bytes 575 | .if NARGS != 11 576 | .printt "missing parameter\n" 577 | .fail 578 | .endif 579 | TestData \1, <\2, >\2, 0, \3, \4, \5, \6, \7, \8, \9, \10, \11 580 | .endm 581 | .macro TestData4_16 582 | ; 4-byte opcode with 16-bit arg in last two bytes 583 | .if NARGS != 12 584 | .printt "missing parameter\n" 585 | .fail 586 | .endif 587 | TestData \1, \2, <\3, >\3, \4, \5, \6, \7, \8, \9, \10, \11, \12 588 | .endm 589 | ; Strings with control characters 590 | .define STREND 0 591 | .define NEWLINE 13 592 | .asciitable 593 | map ' ' to '~' = 32 ; Our font mostly covers 7-bit "ASCII" 594 | .enda 595 | .macro MessageString args s 596 | .asc s, STREND 597 | .if s.length >= 21 598 | .print "too long: ", s, "\n" 599 | .endif 600 | .endm 601 | 602 | ; We use a macro to store these because they have to be big-endian... 603 | .macro CRC 604 | .db (\1>>24)&$ff, (\1>>16)&$ff, (\1>>8)&$ff, \1&$ff 605 | .endm 606 | 607 | .macro CRCs 608 | .ifdef UndocumentedFlags 609 | CRC \2 610 | .else 611 | CRC \1 612 | .endif 613 | .endm 614 | 615 | ; hl, (72704/74752 cases) 616 | ; Opcode: 617 | ; $ed %01sso010 618 | ; o = 0 for sbc, 1 for adc 619 | ; ss = bc|de|hl|sp 620 | adc16: 621 | ; <-- opcode ---> 622 | TestData2 $ed, %01000010, $832c, $4f88, $f22b, $b339, $7e1f, $1563, $d3, $89, $465e 623 | TestData2 0, %00111000, 0, 0, 0, $f821, 0, 0, 0, 0, 0 ; 10 bits -> 1024 permutations 624 | TestData2 0, 0, 0, 0, 0, $ffff, $ffff, $ffff, FlagMask, 0, $ffff ; 70/72 bits -> 71/74 permutations 625 | CRCs $f39089a0 $638e3f1e 626 | .db FlagMask 627 | MessageString "adc/sbc hl,rp" 628 | 629 | ; add hl, (36352/37376 cases) 630 | ; Opcode: 631 | ; $00ss1001 632 | ; ss = bc|de|hl|sp 633 | add16: 634 | ; <-opcode -> 635 | TestData1 %00001001, $c4a5, $c4c7, $d226, $a050, $58ea, $8566, $c6, $de, $9bc9 636 | TestData1 %00110000, 0, 0, 0, $f821, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 637 | TestData1 0, 0, 0, 0, $ffff, $ffff, $ffff, FlagMask, 0, $ffff ; 70/72 bits -> 71/73 permutations 638 | CRCs $1165fc90 $de0ad40e 639 | .db FlagMask 640 | MessageString "add hl,rp" 641 | 642 | ; add ix, (36352/37376 cases) 643 | ; Opcode: 644 | ; $dd $00ss1001 645 | ; ss = bc|de|hl|sp 646 | add16x: 647 | ; <-- opcode ---> 648 | TestData2 $dd, %00001001, $ddac, $c294, $635b, $33d3, $6a76, $fa20, $94, $68, $36f5 649 | TestData2 0, %00110000, 0, 0, $f821, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 650 | TestData2 0, 0, 0, 0, $ffff, 0, $ffff, $ffff, FlagMask, 0, $ffff ; 70/72 bits -> 71/73 permutations 651 | CRCs $c359f7a2 $d6762c69 652 | .db FlagMask 653 | MessageString "add ix,rp" 654 | 655 | ; add iy, (36352/37376 cases) 656 | ; Opcode: 657 | ; $fd $00ss1001 658 | ; ss = bc|de|hl|sp 659 | add16y: 660 | ; <-- opcode ---> 661 | TestData2 $fd, %00001001, $c7c2, $f407, $51c1, $3e96, $0bf4, $510f, $92, $1e, $71ea 662 | TestData2 0, %00110000, 0, $f821, 0, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 663 | TestData2 0, 0, 0, $ffff, 0, 0, $ffff, $ffff, FlagMask, 0, $ffff ; 70/72 bits -> 71/73 permutations 664 | CRCs $5fc828e9 $70deb8b0 665 | .db FlagMask 666 | MessageString "add iy,rp" 667 | 668 | ; aluop a, nn (30720/34816 cases) 669 | ; Opcodes: 670 | ; %11[000]110 $nn add a, n 671 | ; %11[001]110 $nn adc a, n 672 | ; %11[010]110 $nn sub n 673 | ; %11[011]110 $nn sbc a, n 674 | ; %11[100]110 $nn and n 675 | ; %11[101]110 $nn xor n 676 | ; %11[110]110 $nn or n 677 | ; %11[111]110 $nn cp n 678 | alu8i: 679 | ; <---opcode---> 680 | TestData2 %11000110, 0, $9140, $7e3c, $7a67, $df6d, $5b61, $0b29, $10, $66, $85b2 681 | TestData2 %00111000, 0, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 682 | TestData2 0, $ff, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 14/16 bits -> 15/17 permutations 683 | CRCs $48799360 $42d6db16 684 | .db FlagMask 685 | MessageString "aluop a,n" 686 | 687 | ; aluop a, (229376 cases) 688 | ; Opcodes: 689 | ; %10[000]sss add a, sss 690 | ; %10[001]sss adc a, sss 691 | ; %10[010]sss sub sss 692 | ; %10[011]sss sbc sss 693 | ; %10[100]sss and sss 694 | ; %10[101]sss xor sss 695 | ; %10[110]sss or sss 696 | ; %10[111]sss cp sss 697 | ; sss = b|c|d|e|h|l|(hl)|a 698 | ; We split this into 8 tests to speed things up... 699 | alu8r_b: ; (30720/34816 cases) 700 | ; <-opcode-> 701 | TestData1 %10000000, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 702 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 703 | TestData1 0, 0, 0, 0, 0, 0, $ff00, FlagMask, 0, 0 ; 14/16 bits -> 15/17 permutations 704 | CRCs $7f650a22 $aee97b6e 705 | .db FlagMask 706 | MessageString "aluop a,b" 707 | alu8r_c: ; (30720/34816 cases) 708 | ; <-opcode-> 709 | TestData1 %10000001, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 710 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 711 | TestData1 0, 0, 0, 0, 0, 0, $00ff, FlagMask, 0, 0 ; 14 bits -> 15 permutations 712 | CRCs $55ddfdc2 $e43830ce 713 | .db FlagMask 714 | MessageString "aluop a,c" 715 | alu8r_d: ; (30720/34816 cases) 716 | ; <-opcode-> 717 | TestData1 %10000010, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 718 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 719 | TestData1 0, 0, 0, 0, 0, $ff00, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 720 | CRCs $b7145360 $bb297239 721 | .db FlagMask 722 | MessageString "aluop a,d" 723 | alu8r_e: ; (30720/34816 cases) 724 | ; <-opcode-> 725 | TestData1 %10000011, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 726 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 727 | TestData1 0, 0, 0, 0, 0, $00ff, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 728 | CRCs $babc2329 $e344fd93 729 | .db FlagMask 730 | MessageString "aluop a,e" 731 | alu8r_h: ; (30720/34816 cases) 732 | ; <-opcode-> 733 | TestData1 %10000100, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 734 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 735 | TestData1 0, 0, 0, 0, $ff00, 0, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 736 | CRCs $9f2277c2 $2cf27318 737 | .db FlagMask 738 | MessageString "aluop a,h" 739 | alu8r_l: ; (30720/34816 cases) 740 | ; <-opcode-> 741 | TestData1 %10000101, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 742 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 743 | TestData1 0, 0, 0, 0, $00ff, 0, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 744 | CRCs $f72e37a6 $dd48e4c9 745 | .db FlagMask 746 | MessageString "aluop a,l" 747 | alu8r_hl: ; (30720/34816 cases) 748 | ; <-opcode-> 749 | TestData1 %10000110, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 750 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 751 | TestData1 0, $00ff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 752 | CRCs $b2fe080d $f2b6122f 753 | .db FlagMask 754 | MessageString "aluop a,(hl)" 755 | alu8r_a: ; (14336/18432 cases) 756 | ; <-opcode-> 757 | TestData1 %10000111, $c53e, $573a, $4c4d, MachineStateBeforeTest.memop, $e309, $a666, $d0, $3b, $adbb 758 | TestData1 %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 759 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6/8 bits -> 7/9 permutations 760 | CRCs $b51a3dca $0666c711 761 | .db FlagMask 762 | MessageString "aluop a,a" 763 | 764 | ; aluop a, (122880 cases) 765 | ; Opcodes: 766 | ; <$dd|$fd> %10[000]10[x] add a, i[xy][hl] 767 | ; <$dd|$fd> %10[001]10[x] adc a, i[xy][hl] 768 | ; <$dd|$fd> %10[010]10[x] sub i[xy][hl] 769 | ; <$dd|$fd> %10[011]10[x] sbc i[xy][hl] 770 | ; <$dd|$fd> %10[100]10[x] and i[xy][hl] 771 | ; <$dd|$fd> %10[101]10[x] xor i[xy][hl] 772 | ; <$dd|$fd> %10[110]10[x] or i[xy][hl] 773 | ; <$dd|$fd> %10[111]10[x] cp i[xy][hl] 774 | alu8rx_ixh: ; (30720/34816 cases) 775 | ; <---opcode---> 776 | TestData2 $dd, %10000100, $d6f7, $c76e, $accf, $2847, $22dd, $c035, $c5, $38, $234b 777 | TestData2 0, %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 778 | TestData2 0, 0, 0, 0, $ff00, 0, 0, 0, FlagMask, 0, 0 ; 14/16 bits -> 15/17 permutations 779 | CRCs $371ff823 $77e68227 780 | .db FlagMask 781 | MessageString "aluop a,ixh" 782 | alu8rx_ixl: ; (30720/34816 cases) 783 | ; <---opcode---> 784 | TestData2 $dd, %10000101, $d6f7, $c76e, $accf, $2847, $22dd, $c035, $c5, $38, $234b 785 | TestData2 0, %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 786 | TestData2 0, 0, 0, 0, $00ff, 0, 0, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 787 | CRCs $7b057fec $ccdc1a30 788 | .db FlagMask 789 | MessageString "aluop a,ixl" 790 | alu8rx_iyh: ; (30720/34816 cases) 791 | ; <---opcode---> 792 | TestData2 $fd, %10000100, $d6f7, $c76e, $accf, $2847, $22dd, $c035, $c5, $38, $234b 793 | TestData2 0, %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 794 | TestData2 0, 0, 0, $ff00, 0, 0, 0, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 795 | CRCs $9ee7f6c4 $d9612ebe 796 | .db FlagMask 797 | MessageString "aluop a,iyh" 798 | alu8rx_iyl: ; (30720/34816 cases) 799 | ; <---opcode---> 800 | TestData2 $fd, %10000101, $d6f7, $c76e, $accf, $2847, $22dd, $c035, $c5, $38, $234b 801 | TestData2 0, %00111000, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 11 bits -> 2048 permutations 802 | TestData2 0, 0, 0, $00ff, 0, 0, 0, 0, FlagMask, 0, 0 ; 14 bits -> 15 permutations 803 | CRCs $27ec13a6 $3d6a4e44 804 | .db FlagMask 805 | MessageString "aluop a,iyl" 806 | 807 | ; aluop a, (+1) (245760/278562 cases) 808 | ; Opcodes: 809 | ; <$dd|$fd> %10[000]110 $oo add a, (i[xy]+o) 810 | ; <$dd|$fd> %10[001]110 $oo adc a, (i[xy]+o) 811 | ; <$dd|$fd> %10[010]110 $oo sub (i[xy]+o) 812 | ; <$dd|$fd> %10[011]110 $oo sbc (i[xy]+o) 813 | ; <$dd|$fd> %10[100]110 $oo and (i[xy]+o) 814 | ; <$dd|$fd> %10[101]110 $oo xor (i[xy]+o) 815 | ; <$dd|$fd> %10[110]110 $oo or (i[xy]+o) 816 | ; <$dd|$fd> %10[111]110 $oo cp (i[xy]+o) 817 | ; ix and iy point at either memop or memop ^ 1 818 | ; but the test only flips bits in the low byte 819 | alu8x: 820 | ; <-----opcode-----> 821 | TestData3 $dd, %10000110, +1, $90b7, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $32fd, $406e, $c1dc, $45, $6e, $e5fa 822 | TestData3 $20, %00111000, 0, 0, 1, 1, 0, 0, 0, 0, $ff, 0 ; 14 bits -> 16384 permutations 823 | TestData3 0, 0, 0, $00ff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 14/16 bits -> 15/17 permutations 824 | CRCs $2bc2d52d $6c506ef4 825 | .db FlagMask 826 | MessageString "aluop a,(ix/y+1)" 827 | 828 | ; bit n, (+1) (2304 cases) 829 | ; Opcode: 830 | ; <$dd|$fd> $cb $oo %01nnn110 831 | ; ix and iy point to one byte before memop, and we cycle the bits in the following byte 832 | bitx: 833 | ; <--------opcode-------> 834 | TestData $dd, $cb, +1, %01000110, $2075, MachineStateBeforeTest.memop-1, MachineStateBeforeTest.memop-1, $3cfc, $a79a, $3d74, $51, $27, $ca14 835 | TestData $20, 0, 0, %00111000, 0, 0, 0, 0, 0, 0, $53, 0, 0 ; 8 bits -> 256 permutations 836 | TestData 0, 0, 0, 0, $00ff, 0, 0, 0, 0, 0, 0, 0, 0 ; 8 bits -> 9 permutations 837 | CRCs $55c9ea76 $55c9ea76 838 | .db FlagMask 839 | MessageString "bit n,(ix/y+1)" 840 | 841 | ; bit n, (50176 cases) 842 | ; Opcode: 843 | ; $cb %01nnnsss 844 | ; nnn = bit number 845 | ; sss = b|c|d|e|h|l|(hl)|a 846 | bitz80: 847 | ; <---opcode---> 848 | TestData2 $cb, %01000000, $3ef1, $9dfc, $7acc, MachineStateBeforeTest.memop, $be61, $7a86, $50, $24, $1998 849 | TestData2 0, %00111111, 0, 0, 0, 0, 0, 0, $53, 0, 0 ; 10 bits -> 1024 permutations 850 | TestData2 0, 0, $00ff, 0, 0, 0, $ffff, $ffff, 0, $ff, 0 ; 48 bits -> 49 permutations 851 | CRCs $4b37451d $a937a161 852 | .db FlagMask 853 | MessageString "bit n,r/(hl)" 854 | 855 | ; cpd (1) (14336/18432 cases) 856 | ; Opcodes: 857 | ; $ed $a9 cpd = cp (hl); dec hl; dec bc 858 | ; $ed $b9 cpdr = cpd until bc = 0 859 | ; hl points to the end of the machine state 860 | ; bc is set to 1, 3, 9, 11 - originally due to a porting error, originally only 1 and 9 were tested 861 | ; a and f are permuted 862 | cpd1: 863 | ; 864 | TestData2 $ed, $a9, $c7b6, $72b4, $18f6, MachineStateBeforeTest.sp, $8dbd, 1, $c0, $30, $94a3 865 | TestData2 0, $10, 0, 0, 0, 0, 0, 10, 0, $ff, 0 ; 11 bits -> 2048 permutations 866 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6/8 bits -> 7/9 permutations 867 | CRCs $6b7eb6bf $1000887f 868 | .db FlagMask 869 | MessageString "cpd/cpdr" 870 | 871 | ; cpi (1) (14336 cases) 872 | ; Opcodes: 873 | ; $ed $a1 cpi = cp (hl); inc hl; dec bc 874 | ; $ed $b1 cpir = cpd until bc = 0 875 | ; hl points to the end of the machine state 876 | ; bc is set to 1, 3, 9, 11 - due to a porting error, originally only 1 and 9 were tested 877 | ; a and f are permuted 878 | cpi1: 879 | ; 880 | TestData2 $ed, $a1, $4d48, $af4a, $906b, MachineStateBeforeTest.memop, $4e71, 1, $93, $6a, $907c 881 | TestData2 0, $10, 0, 0, 0, 0, 0, 10, 0, $ff, 0 ; 11 bits -> 2048 permutations 882 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 883 | CRCs $74baf310 $67595505 884 | .db FlagMask 885 | MessageString "cpi/cpir" 886 | 887 | ; (128 cases) 888 | ; Opcodes: 889 | ; $37 scf 890 | ; $3f ccf 891 | sccf: 892 | ; 893 | TestData1 $37, $2141, $09fa, $1d60, $a559, $8d5b, $9079, $04, $8e, $299d 894 | TestData1 $08, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 7 bits -> 128 permutations 895 | TestData1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 896 | CRCs $70b745fb $2d268622 897 | ; Note that SCF/CCF undocumented flags results vary by CPU, and on some 898 | ; CPUs/systems are not consistent. Thus we filter them out even in 899 | ; "undocumented flags mode". 900 | .db FlagMask & %11010111 901 | MessageString "scf/ccf" 902 | 903 | ; cpl (256 cases) 904 | ; Opcode: 905 | ; $2f 906 | cplop: 907 | ; 908 | TestData1 $2f, $2141, $09fa, $1d60, $a559, $8d5b, $9079, $04, $8e, $299d 909 | TestData1 0, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 8 bits -> 256 permutations 910 | TestData1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 911 | CRCs $63ae5a13 $c5ef74f9 912 | .db FlagMask 913 | MessageString "cpl" 914 | 915 | ; daa (16384/65536 cases) 916 | ; Opcode: 917 | ; $27 918 | daaop: 919 | ; 920 | TestData1 $27, $2141, $09fa, $1d60, $a559, $8d5b, $9079, $04, $8e, $299d 921 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, $ff, 0 ; 14/16 bits -> 16384/65536 permutations 922 | TestData1 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 923 | CRCs $a4611558 $93c38500 924 | .db FlagMask 925 | MessageString "daa" 926 | 927 | 928 | ; a (3584/4608 cases) 929 | ; Opcodes: 930 | ; $3c inc a 931 | ; $3d dec a 932 | ; All values in a are tested, with each flag bit tested in isolation. 933 | inca: 934 | ; 935 | TestData1 $3c, $4adf, $d5d8, $e598, $8a2b, $a7b0, $431b, $44, $5a, $d030 936 | TestData1 $01, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 9 bits -> 512 permutations 937 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6/8 bits -> 7/9 permutations 938 | CRCs $d18815a4 $895cbf57 939 | .db FlagMask 940 | MessageString "inc/dec a" 941 | 942 | ; b (3584 cases) 943 | ; Opcodes: 944 | ; $04 inc b 945 | ; $05 dec b 946 | ; All values in b are tested, with each flag bit tested in isolation. 947 | incb: 948 | ; 949 | TestData1 $04, $d623, $432d, $7a61, $8180, $5a86, $1e85, $86, $58, $9bbb 950 | TestData1 $01, 0, 0, 0, 0, 0, $ff00, 0, 0, 0 ; 9 bits -> 512 permutations 951 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 952 | CRCs $5f682264 $8842f7a3 953 | .db FlagMask 954 | MessageString "inc/dec b" 955 | 956 | ; bc (1792/2304 cases) 957 | ; Opcodes: 958 | ; $03 inc bc 959 | ; $0b dec bc 960 | ; Only 256 values in bc are tested, with each flag bit tested in isolation. 961 | incbc: 962 | ; 963 | TestData1 $03, $cd97, $44ab, $8dc9, $e3e3, $11cc, $e8a4, $02, $49, $2a4d 964 | TestData1 $08, 0, 0, 0, 0, 0, $f821, 0, 0, 0 ; 8 bits -> 256 permutations 965 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6/8 bits -> 7/9 permutations 966 | CRCs $d2ae3bec $7c28865e 967 | .db FlagMask 968 | MessageString "inc/dec bc" 969 | 970 | ; c (3584 cases) 971 | ; Opcodes: 972 | ; $0c inc c 973 | ; $0d dec c 974 | ; All values in c are tested, with each flag bit tested in isolation. 975 | incc: 976 | ; 977 | TestData1 $0c, $d789, $0935, $055b, $9f85, $8b27, $d208, $95, $05, $0660 978 | TestData1 $01, 0, 0, 0, 0, 0, $00ff, 0, 0, 0 ; 9 bits -> 512 permutations 979 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 980 | CRCs $c284554c $28cab011 981 | .db FlagMask 982 | MessageString "inc/dec c" 983 | 984 | ; d (3584 cases) 985 | ; Opcodes: 986 | ; $14 inc d 987 | ; $15 dec d 988 | ; All values in d are tested, with each flag bit tested in isolation. 989 | incd: 990 | ; 991 | TestData1 $14, $a0ea, $5fba, $65fb, $981c, $38cc, $debc, $43, $5c, $03bd 992 | TestData1 $01, 0, 0, 0, 0, $ff00, 0, 0, 0, 0 ; 9 bits -> 512 permutations 993 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 994 | CRCs $4523de10 $e3588b40 995 | .db FlagMask 996 | MessageString "inc/dec d" 997 | 998 | ; de (1792 cases) 999 | ; Opcodes: 1000 | ; $13 inc de 1001 | ; $1b dec de 1002 | ; Only 256 values in de are tested, with each flag bit tested in isolation. 1003 | incde: 1004 | ; 1005 | TestData1 $13, $342e, $131d, $28c9, $0aca, $9967, $3a2e, $92, $f6, $9d54 1006 | TestData1 $08, 0, 0, 0, 0, $f821, 0, 0, 0, 0 ; 8 bits -> 256 permutations 1007 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1008 | CRCs $aec6d42c $c6d64c1f 1009 | .db FlagMask 1010 | MessageString "inc/dec de" 1011 | 1012 | ; e (3584 cases) 1013 | ; Opcodes: 1014 | ; $1c inc e 1015 | ; $1d dec e 1016 | ; All values in e are tested, with each flag bit tested in isolation. 1017 | ince: 1018 | ; 1019 | TestData1 $1c, $602f, $4c0d, $2402, $e2f5, $a0f4, $a10a, $13, $32, $5925 1020 | TestData1 $01, 0, 0, 0, 0, $00ff, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1021 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1022 | CRCs $e175afcc $460ecf92 1023 | .db FlagMask 1024 | MessageString "inc/dec e" 1025 | 1026 | ; h (3584 cases) 1027 | ; Opcodes: 1028 | ; $24 inc h 1029 | ; $25 dec h 1030 | ; All values in h are tested, with each flag bit tested in isolation. 1031 | inch: 1032 | ; 1033 | TestData1 $24, $1506, $f2eb, $e8dd, $262b, $11a6, $bc1a, $17, $06, $2818 1034 | TestData1 $01, 0, 0, 0, $ff00, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1035 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1036 | CRCs $1ced847d $efa0f9dc 1037 | .db FlagMask 1038 | MessageString "inc/dec h" 1039 | 1040 | ; hl (1792 cases) 1041 | ; Opcodes: 1042 | ; $23 inc hl 1043 | ; $2b dec hl 1044 | ; Only 256 values in hl are tested, with each flag bit tested in isolation. 1045 | inchl: 1046 | ; 1047 | TestData1 $23, $c3f4, $07a5, $1b6d, $4f04, $e2c2, $822a, $57, $e0, $c3e1 1048 | TestData1 $08, 0, 0, 0, $f821, 0, 0, 0, 0, 0 ; 8 bits -> 256 permutations 1049 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1050 | CRCs $fc0d6d4a $f489fab6 1051 | .db FlagMask 1052 | MessageString "inc/dec hl" 1053 | 1054 | ; ix (1792 cases) 1055 | ; Opcodes: 1056 | ; $dd $23 inc ix 1057 | ; $dd $2b dec ix 1058 | ; Only 256 values in ix are tested, with each flag bit tested in isolation. 1059 | incix: 1060 | ; 1061 | TestData2 $dd, $23, $bc3c, $0d9b, $e081, $adfd, $9a7f, $96e5, $13, $85, $0be2 1062 | TestData2 0, $08, 0, 0, $f821, 0, 0, 0, 0, 0, 0 ; 8 bits -> 256 permutations 1063 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1064 | CRCs $a54dbe31 $f1886204 1065 | .db FlagMask 1066 | MessageString "inc/dec ix" 1067 | 1068 | ; iy (1792 cases) 1069 | ; Opcodes: 1070 | ; $fd $23 inc iy 1071 | ; $fd $2b dec iy 1072 | ; Only 256 values in iy are tested, with each flag bit tested in isolation. 1073 | inciy: 1074 | ; 1075 | TestData2 $fd, $23, $9402, $637a, $3182, $c65a, $b2e9, $abb4, $16, $f2, $6d05 1076 | TestData2 0, $08, 0, $f821, 0, 0, 0, 0, 0, 0, 0 ; 8 bits -> 256 permutations 1077 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1078 | CRCs $505d51a3 $57eb0ff4 1079 | .db FlagMask 1080 | MessageString "inc/dec iy" 1081 | 1082 | ; l (3584 cases) 1083 | ; Opcodes: 1084 | ; $2c inc l 1085 | ; $2d dec l 1086 | ; All values in l are tested, with each flag bit tested in isolation. 1087 | incl: 1088 | ; 1089 | TestData1 $2c, $8031, $a520, $4356, $b409, $f4c1, $dfa2, $d1, $3c, $3ea2 1090 | TestData1 $01, 0, 0, 0, $00ff, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1091 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1092 | CRCs $56cd06f3 $b6538894 1093 | .db FlagMask 1094 | MessageString "inc/dec l" 1095 | 1096 | ; (hl) (3584 cases) 1097 | ; Opcodes: 1098 | ; $34 inc (hl) 1099 | ; $35 dec (hl) 1100 | ; All values in (hl) are tested, with each flag bit tested in isolation. 1101 | incm: 1102 | ; 1103 | TestData1 $34, $b856, $0c7c, $e53e, MachineStateBeforeTest.memop, $877e, $da58, $15, $5c, $1f37 1104 | TestData1 $01, $00ff, 0, 0, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1105 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1106 | CRCs $46761d6b $640e3d80 1107 | .db FlagMask 1108 | MessageString "inc/dec (hl)" 1109 | 1110 | ; sp (1792 cases) 1111 | ; Opcodes: 1112 | ; $33 inc sp 1113 | ; $3b dec sp 1114 | ; Only 256 values in sp are tested, with each flag bit tested in isolation. 1115 | incsp: 1116 | ; 1117 | TestData1 $33, $346f, $d482, $d169, $deb6, $a494, $f476, $53, $02, $855b 1118 | TestData1 $08, 0, 0, 0, 0, 0, 0, 0, 0, $f821 ; 8 bits -> 256 permutations 1119 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1120 | CRCs $5dacd527 $cb40b182 1121 | .db FlagMask 1122 | MessageString "inc/dec sp" 1123 | 1124 | ; (+1) (7168/9216 cases) 1125 | incx: 1126 | ; Opcodes: 1127 | ; <$dd|$fd> $34 1 inc (i[xy]+1) 1128 | ; <$dd|$fd> $35 1 dec (i[xy]+1) 1129 | ; ix+1 and iy+1 point at the low byte of memop 1130 | ; <--opcode--> 1131 | TestData3 $dd, $34, 1, $fa6e, MachineStateBeforeTest.memop-1, MachineStateBeforeTest.memop-1, $2c28, $8894, $5057, $16, $33, $286f 1132 | TestData3 $20, $01, 0, $00ff, 0, 0, 0, 0, 0, 0, 0, 0 ; 10 bits -> 1024 permutations 1133 | TestData3 0, 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1134 | CRCs $8897c715 $8d249a60 1135 | .db FlagMask 1136 | MessageString "inc/dec (ix/y+1)" 1137 | 1138 | ; ixh (3584 cases) 1139 | ; Opcodes: 1140 | ; $dd $24 inc ixh (undocumented) 1141 | ; $dd $25 dec ixh (undocumented) 1142 | ; All values in ixh are tested, with each flag bit tested in isolation. 1143 | incxh: 1144 | ; 1145 | TestData2 $dd, $24, $b838, $316c, $c6d4, $3e01, $8358, $15b4, $81, $de, $4259 1146 | TestData2 0, $01, 0, 0, $ff00, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1147 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1148 | CRCs $cfc8b622 $0f53b47d 1149 | .db FlagMask 1150 | MessageString "inc/dec ixh" 1151 | 1152 | ; ixl (3584 cases) 1153 | ; Opcodes: 1154 | ; $dd $2c inc ixl (undocumented) 1155 | ; $dd $2d dec ixl (undocumented) 1156 | ; All values in ixl are tested, with each flag bit tested in isolation. 1157 | incxl: 1158 | ; 1159 | TestData2 $dd, $2c, $4d14, $7460, $76d4, $06e7, $32a2, $213c, $d6, $d7, $99a5 1160 | TestData2 0, $01, 0, 0, $00ff, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1161 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1162 | CRCs $bb96e4c1 $1d067578 1163 | .db FlagMask 1164 | MessageString "inc/dec ixl" 1165 | 1166 | ; iyh (3584 cases) 1167 | ; Opcodes: 1168 | ; $fd $24 inc iyh (undocumented) 1169 | ; $fd $25 dec iyh (undocumented) 1170 | ; All values in iyh are tested, with each flag bit tested in isolation. 1171 | incyh: 1172 | ; 1173 | TestData2 $fd, $24, $2836, $9f6f, $9116, $61b9, $82cb, $e219, $92, $73, $a98c 1174 | TestData2 0, $01, 0, $ff00, 0, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1175 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1176 | CRCs $580724ce $d6ade161 1177 | .db FlagMask 1178 | MessageString "inc/dec iyh" 1179 | 1180 | ; iyl (3584 cases) 1181 | ; Opcodes: 1182 | ; $fd $2c inc iyl (undocumented) 1183 | ; $fd $2d dec iyl (undocumented) 1184 | ; All values in iyl are tested, with each flag bit tested in isolation. 1185 | incyl: 1186 | ; 1187 | TestData2 $fd, $2c, $d7c6, $62d5, $a09e, $7039, $3e7e, $9f12, $90, $d9, $220f 1188 | TestData2 0, $01, 0, $00ff, 0, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1189 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6 bits -> 7 permutations 1190 | CRCs $29b50d35 $283a9a3d 1191 | .db FlagMask 1192 | MessageString "inc/dec iyl" 1193 | 1194 | ; ld , (addr) (34 cases) 1195 | ; Opcodes: 1196 | ; $ed $4b $nnnn ld bc, (addr) 1197 | ; $ed $5b $nnnn ld hl, (addr) 1198 | ; TODO: could exercise undocumented $ed $6b ld hl, (addr), it's skipped because it's undocumented and a duplicate (of $2a) 1199 | ld161: 1200 | ; <-------------- opcode --------------> 1201 | TestData4_16 $ed, $4b, MachineStateBeforeTest.memop, $f9a8, $f559, $93a4, $f5ed, $6f96, $d968, $86, $e6, $4bd8 1202 | TestData4_16 0, $10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1203 | TestData4_16 0, 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1204 | CRCs $4d45a9ac $4d45a9ac 1205 | .db FlagMask 1206 | MessageString "ld bc/de,(addr)" 1207 | 1208 | ; ld hl, (addr) (17 cases) 1209 | ; Opcode: 1210 | ; $2a $nnnn 1211 | ld162: 1212 | ; <------------ opcode -----------> 1213 | TestData3_16 $2a, MachineStateBeforeTest.memop, $9863, $7830, $2077, $b1fe, $b9fa, $abb8, $04, $06, $6015 1214 | TestData3_16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 1215 | TestData3_16 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1216 | CRCs $5f972487 $5f972487 1217 | .db FlagMask 1218 | MessageString "ld hl,(addr)" 1219 | 1220 | ; ld sp, (addr) (17 cases) 1221 | ; Opcode: 1222 | ; $ed $7b $nnnn 1223 | ld163: 1224 | ; <-------------- opcode --------------> 1225 | TestData4_16 $ed, $7b, MachineStateBeforeTest.memop, $8dfc, $57d7, $2161, $ca18, $c185, $27da, $83, $1e, $f460 1226 | TestData4_16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 1227 | TestData4_16 0, 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1228 | CRCs $7acea11b $7acea11b 1229 | .db FlagMask 1230 | MessageString "ld sp,(addr)" 1231 | 1232 | ; ld , (addr) (34 cases) 1233 | ; Opcode: 1234 | ; <$dd|$fd> $2a $nnnn 1235 | ld164: 1236 | ; <-------------- opcode --------------> 1237 | TestData4_16 $dd, $2a, MachineStateBeforeTest.memop, $ded7, $a6fa, $f780, $244c, $87de, $bcc2, $16, $63, $4c96 1238 | TestData4_16 $20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1239 | TestData4_16 0, 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1240 | CRCs $858bf16d $858bf16d 1241 | .db FlagMask 1242 | MessageString "ld ix/y,(addr)" 1243 | 1244 | ; ld (addr), (66 cases) 1245 | ; Opcodes: 1246 | ; $ed $43 $nnnn ld (addr), bc 1247 | ; $ed $53 $nnnn ld (addr), de 1248 | ld165: 1249 | ; <-------------- opcode --------------> 1250 | TestData4_16 $ed, $43, MachineStateBeforeTest.memop, $1f98, $844d, $e8ac, $c9ed, $c95d, $8f61, $80, $3f, $c7bf 1251 | TestData4_16 0, $10, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1252 | TestData4_16 0, 0, 0, 0, 0, 0, 0, $ffff, $ffff, 0, 0, 0 ; 32 bits -> 33 permutations 1253 | CRCs $641e8715 $641e8715 1254 | .db FlagMask 1255 | MessageString "ld (addr),bc/de" 1256 | 1257 | ; ld (addr), hl (17 cases) 1258 | ; Opcode: 1259 | ; $22 $nnnn 1260 | ld166: 1261 | ; <------------ opcode -----------> 1262 | TestData3_16 $22, MachineStateBeforeTest.memop, $d003, $7772, $7f53, $3f72, $64ea, $e180, $10, $2d, $35e9 1263 | TestData3_16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 1264 | TestData3_16 0, 0, 0, 0, 0, $ffff, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1265 | CRCs $a3608b47 $a3608b47 1266 | .db FlagMask 1267 | MessageString "ld (addr),hl" 1268 | 1269 | ; ld (addr), sp (17 cases) 1270 | ; Opcode: 1271 | ; $ed $73 $nnnn 1272 | ld167: 1273 | ; <-------------- opcode --------------> 1274 | TestData4_16 $ed, $73, MachineStateBeforeTest.memop, $c0dc, $d1d6, $ed5a, $f356, $afda, $6ca7, $44, $9f, $3f0a 1275 | TestData4_16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 1276 | TestData4_16 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, $ffff ; 16 bits -> 17 permutations 1277 | CRCs $16585fd7 $16585fd7 1278 | .db FlagMask 1279 | MessageString "ld (addr),sp" 1280 | 1281 | ; ld (addr), (66 cases) 1282 | ; Opcode: 1283 | ; <$dd|$fd> $22 $nnnn 1284 | ld168: 1285 | ; <-------------- opcode --------------> 1286 | TestData4_16 $dd, $22, MachineStateBeforeTest.memop, $6cc3, $0d91, $6900, $8ef8, $e3d6, $c3f7, $c6, $d9, $c2df 1287 | TestData4_16 $20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1288 | TestData4_16 0, 0, 0, 0, $ffff, $ffff, 0, 0, 0, 0, 0, 0 ; 32 bits -> 33 permutations 1289 | CRCs $ba102a6b $ba102a6b 1290 | .db FlagMask 1291 | MessageString "ld (addr)," 1292 | 1293 | ; ld , nnnn (68 cases) 1294 | ; Opcode: 1295 | ; %00ss0001 $nnnn ld ss, nnnn 1296 | ; ss = bc|de|hl|sp 1297 | ld16im: 1298 | ; <--- opcode ---> 1299 | TestData3_16 %00000001, 0, $5c1c, $2d46, $8eb9, $6078, $74b1, $b30e, $46, $d1, $30cc 1300 | TestData3_16 %00110000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 2 bits -> 4 permutations 1301 | TestData3_16 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1302 | CRCs $de391969 $de391969 1303 | .db FlagMask 1304 | MessageString "ld rp,nnnn" 1305 | 1306 | ; ld , nnnn (34 cases) 1307 | ; Opcode: 1308 | ; <$dd|$fd> $21 $nnnn 1309 | ld16ix: 1310 | ; <--- opcode --> 1311 | TestData4_16 $dd, $21, 0, $87e8, $2006, $bd12, $b69b, $7253, $a1e5, $51, $13, $f1bd 1312 | TestData4_16 $20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1313 | TestData4_16 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1314 | CRCs $227dd525 $227dd525 1315 | .db FlagMask 1316 | MessageString "ld ix/y,nnnn" 1317 | 1318 | ; ld a, <(bc)|(de)> (46 cases) 1319 | ; Opcodes: 1320 | ; $0a ld a, (bc) 1321 | ; $1a ld a, (de) 1322 | ld8bd: 1323 | ; 1324 | TestData1 $0a, $b3a8, $1d2a, $7f8e, $42ac, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $c6, $b1, $ef8e 1325 | TestData1 $10, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1326 | TestData1 0, $00ff, 0, 0, 0, 0, 0, FlagMask, $ff, 0 ; 22 bits -> 23 permutations 1327 | CRCs $2439f60d $dc37bba6 1328 | .db FlagMask 1329 | MessageString "ld a,(bc/de)" 1330 | 1331 | ; ld , nn (72 cases) 1332 | ; Opcode: 1333 | ; %00sss110 $nn 1334 | ; sss = b|c|d|e|h|l|(hl)|a 1335 | ld8im: 1336 | ; 1337 | TestData2 $06, 0, $c407, $f49d, $d13d, MachineStateBeforeTest.memop, $de89, $7455, $53, $c0, $5509 1338 | TestData2 $38, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 3 bits -> 8 permutations 1339 | TestData2 0, $ff, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 8 bits -> 9 permutations 1340 | CRCs $df535f2a $df535f2a 1341 | .db FlagMask 1342 | MessageString "ld r/(hl),n" 1343 | 1344 | ; ld (+1), nn (18 cases) 1345 | ; Opcode: 1346 | ; <$dd|$fd> $36 $oo $nn 1347 | ld8imx: 1348 | ; <----opcode----> 1349 | TestData $dd, $36, 1, $00, $1b45, MachineStateBeforeTest.memop-1, MachineStateBeforeTest.memop-1, $d5c1, $61c7, $bdc4, $c0, $85, $cd16 1350 | TestData $20, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1351 | TestData 0, 0, 0, $ff, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 8 bits -> 9 permutations 1352 | CRCs $728e38cf $728e38cf 1353 | .db FlagMask 1354 | MessageString "ld (ix/y+1),nn" 1355 | 1356 | ; ld , (+1) (1088 cases) 1357 | ; Opcode: 1358 | ; <$dd|$fd> %010ss110 $oo 1359 | ; ss = b|c|d|e 1360 | ; Tests offsets 0 and 1 and also moves ix and iy on by 1 so it reads from iyl sometimes 1361 | ld8ix1: 1362 | ; <-----opcode----> 1363 | TestData3 $dd, %01000110, 0, $d016, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $4260, $7f39, $0404, $97, $4a, $d085 1364 | TestData3 $20, %00011000, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 ; 6 bits -> 64 permutations 1365 | TestData3 0, 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1366 | CRCs $6db05c44 $6db05c44 1367 | .db FlagMask 1368 | MessageString "ld b/c/d/e,(ix/y+1)" 1369 | 1370 | ; ld , (+1) (544 cases) 1371 | ; Opcode: 1372 | ; <$dd|$fd> %011s110 $oo 1373 | ; s = h|l 1374 | ; Tests offsets 0 and 1 and also moves ix and iy on by 1 so it reads from iyl sometimes 1375 | ld8ix2: 1376 | ; <-----opcode-----> 1377 | TestData3 $dd, %01100110, 0, $84e0, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $9c52, $a799, $49b6, $93, $00, $eead 1378 | TestData3 $20, %00001000, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 ; 5 bits -> 32 permutations 1379 | TestData3 0, 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1380 | CRCs $3e094165 $3e094165 1381 | .db FlagMask 1382 | MessageString "ld h/l,(ix/y+1)" 1383 | 1384 | ; ld a, (+1) (272 cases) 1385 | ; Opcode: 1386 | ; <$dd|$fd> $7e $oo 1387 | ; Tests offsets 0 and 1 and also moves ix and iy on by 1 so it reads from iyl sometimes 1388 | ld8ix3: 1389 | ; <--opcode--> 1390 | TestData3 $dd, $7e, 0, $d8b6, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $c612, $df07, $9cd0, $43, $a6, $a0e5 1391 | TestData3 $20, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0 ; 4 bits -> 16 permutations 1392 | TestData3 0, 0, 0, $ffff, 0, 0, 0, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1393 | CRCs $5407eb38 $5407eb38 1394 | .db FlagMask 1395 | MessageString "ld a,(ix/y+1)" 1396 | 1397 | ; ld , nn (1024 cases) 1398 | ; Opcodes: 1399 | ; <$dd|$fd> $26 $nn ld i[xy]h, n 1400 | ; <$dd|$fd> $2e $nn ld i[xy]l, n 1401 | ld8ixy: 1402 | ; <---opcode---> 1403 | TestData3 $dd, $26, $00, $3c53, $4640, $e179, $7711, $c107, $1afa, $81, $ad, $5d9b 1404 | TestData3 $20, $08, $ff, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 10 bits -> 1024 permutations 1405 | TestData3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutation 1406 | CRCs $567e302a $567e302a 1407 | .db FlagMask 1408 | MessageString "ld ix/yh/l,nn" 1409 | 1410 | ; ld , (3520 cases) 1411 | ; Opcode: 1412 | ; %01sssttt ld s, t 1413 | ; sss = b|c|d|e|h|l|(hl)|a 1414 | ; ttt = b|c|d|e|h|l|(hl)|a 1415 | ld8rr: 1416 | ; <-opcode> 1417 | TestData1 %01000000, $72a4, $a024, $61ac, MachineStateBeforeTest.memop, $82c7, $718f, $97, $8f, $ef8e 1418 | TestData1 %00111111, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 6 bits -> 64 permutations 1419 | TestData1 0, $00ff, 0, 0, 0, $ffff, $ffff, FlagMask, $ff, 0 ; 54/56 bits -> 55/57 permutations 1420 | CRCs $5d1e1c64 $1842d84b 1421 | .db FlagMask 1422 | MessageString "ld r/(hl),r/(hl)" 1423 | 1424 | ; ld , (7040 cases) 1425 | ; Opcode: 1426 | ; <$dd|$fd> %01sssttt ($oo) ld s, t 1427 | ; sss = b|c|d|e|i[xy]h|i[xy]l|(i[xy]+o)|a 1428 | ; ttt = b|c|d|e|i[xy]h|i[xy]l|(i[xy]+o)|a 1429 | ; Index offset parameter is used by some opcodes, so it's left at 0 1430 | ; so it's a nop for the rest. These offsetting opcodes are tested 1431 | ; elsewhere. The non-offsetting ones are undocumented. 1432 | ld8rrx: 1433 | ; <-----opcode-----> 1434 | TestData3 $dd, %01000000, 0, $bcc5, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $2fc2, $98c0, $83, $1f, $3bcd 1435 | TestData3 $20, %00111111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 7 bits -> 128 permutations 1436 | TestData3 0, 0, 0, $00ff, 0, 0, 0, $ffff, $ffff, FlagMask, $ff, 0 ; 54/56 bits -> 55/57 permutations 1437 | CRCs $4c9e4b7b $2bbb0252 1438 | .db FlagMask 1439 | MessageString "ld r/(ix/y),r/(ix/y)" 1440 | 1441 | ; ld a, (addr) / ld (addr), a (46 cases) 1442 | ; Opcodes: 1443 | ; $32 $nnnn ld a, (addr) 1444 | ; $3a $nnnn ld (addr), a 1445 | lda: 1446 | ; <------------ opcode -----------> 1447 | TestData3_16 $32, MachineStateBeforeTest.memop, $fd68, $f4ec, $44a0, $b543, $0653, $cdba, $d2, $4f, $1fd8 1448 | TestData3_16 $08, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1449 | TestData3_16 0, 0, $00ff, 0, 0, 0, 0, 0, FlagMask, $ff, 0 ; 22/24 bits -> 23/25 permutations 1450 | CRCs $c9262de5 $063adbd9 1451 | .db FlagMask 1452 | MessageString "ld a,(ad)/ld (ad),a" 1453 | 1454 | ; ldd (1) (46 cases) 1455 | ; Opcodes: 1456 | ; $ed $a8 ldd 1457 | ; $ed $b8 lddr 1458 | ; Copies a byte within the machine state from iyh to memop high, in both cases (bc = 1) 1459 | ; TODO: permutes unaccessed memop? 1460 | ldd1: 1461 | ; 1462 | TestData2 $ed, $a8, $9852, $68fa, $66a1, MachineStateBeforeTest+3, MachineStateBeforeTest+1, 1, $c1, $68, $20b7 1463 | TestData2 0, $10, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1464 | TestData2 0, 0, $ffff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 22/24 bits -> 23/25 permutations 1465 | CRCs $f82148b7 $03a4fb21 1466 | .db FlagMask 1467 | MessageString "ldd/lddr (1)" 1468 | 1469 | ; ldd (2) (46 cases) 1470 | ; Opcodes: 1471 | ; $ed $a8 ldd 1472 | ; $ed $b8 lddr 1473 | ; Copies a byte or two within the machine state from iyh to memop high (bc = 2) 1474 | ; TODO: permutes unaccessed memop? 1475 | ldd2: 1476 | ; 1477 | TestData2 $ed, $a8, $f12e, $eb2a, $d5ba, MachineStateBeforeTest+3, MachineStateBeforeTest+1, 2, $47, $ff, $fbe4 1478 | TestData2 0, $10, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1479 | TestData2 0, 0, $ffff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 22 bits -> 23 permutations 1480 | CRCs $e22ab30f $e4e46a92 1481 | .db FlagMask 1482 | MessageString "ldd/lddr (2)" 1483 | 1484 | ; ldi (1) (46 cases) 1485 | ; Opcodes: 1486 | ; $ed $a0 ldi 1487 | ; $ed $b0 ldir 1488 | ; Copies a byte within the machine state from iyl to memop low (bc = 1) 1489 | ; TODO: permutes unaccessed memop? 1490 | ldi1: 1491 | ; 1492 | TestData2 $ed, $a0, $fe30, $03cd, $0006, MachineStateBeforeTest.iy, MachineStateBeforeTest.memop, 1, $04, $60, $2688 1493 | TestData2 0, $10, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1494 | TestData2 0, 0, $ffff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 22 bits -> 23 permutations 1495 | CRCs $470098d4 $126e18dc 1496 | .db FlagMask 1497 | MessageString "ldi/ldir (1)" 1498 | 1499 | ; ldi (2) (46 cases) 1500 | ; Opcodes: 1501 | ; $ed $a0 ldi 1502 | ; $ed $b0 ldir 1503 | ; Copies a byte or two within the machine state from iyl to memop low (bc = 2) 1504 | ; TODO: permutes unaccessed memop? 1505 | ldi2: 1506 | ; 1507 | TestData2 $ed, $a0, $4ace, $c26e, $b188, MachineStateBeforeTest.iy, MachineStateBeforeTest.memop, 2, $14, $2d, $a39f 1508 | TestData2 0, $10, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 1 bit -> 2 permutations 1509 | TestData2 0, 0, $ffff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 22 bits -> 23 permutations 1510 | CRCs $382fa523 $3456db83 1511 | .db FlagMask 1512 | MessageString "ldi/ldir (2)" 1513 | 1514 | ; neg (16384 cases) 1515 | ; Opcode: 1516 | ; $ed $44 1517 | ; Tests all values of a and flags 1518 | negop: 1519 | ; 1520 | TestData2 $ed, $44, $38a2, $5f6b, $d934, $57e4, $d2d6, $4642, $43, $5a, $09cc 1521 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, $ff, 0 ; 14/16 bits -> 16384/65536 permutations 1522 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 0 bits -> 1 permutations 1523 | CRCs $6a3c3bbd $1ef66515 1524 | .db FlagMask 1525 | MessageString "neg" 1526 | 1527 | ; (7680 cases) 1528 | ; Opcodes: 1529 | ; $ed $67 rrd 1530 | ; $ed $6f rld 1531 | rldop: 1532 | ; 1533 | TestData2 $ed, $67, $91cb, $c48b, $fa62, MachineStateBeforeTest.memop, $e720, $b479, $40, $06, $8ae2 1534 | TestData2 0, $08, $00ff, 0, 0, 0, 0, 0, 0, 0, 0 ; 9 bits -> 512 permutations 1535 | TestData2 0, 0, 0, 0, 0, 0, 0, 0, FlagMask, $ff, 0 ; 14/16 bits -> 15/17 permutations 1536 | CRCs $f7da9257 $2766bb62 1537 | .db FlagMask 1538 | MessageString "rrd/rld" 1539 | 1540 | ; (7168 cases) 1541 | ; Opcodes: 1542 | ; $07 rlca 1543 | ; $0f rrca 1544 | ; $17 rla 1545 | ; $1f rra 1546 | rot8080: 1547 | ; 1548 | TestData1 $07, $cb92, $6d43, $0a90, $c284, $0c53, $f50e, $91, $eb, $40fc 1549 | TestData1 $18, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 10 bits -> 1024 permutations 1550 | TestData1 0, 0, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 6/8 bits -> 7/9 permutations 1551 | CRCs $251330ae $fa480129 1552 | .db FlagMask 1553 | MessageString "rlca/rrca/rla/rra" 1554 | 1555 | ; shift/rotate (+1) (448 cases) 1556 | ; Opcodes: 1557 | ; <$dd|$fd> $cb $oo $06 rlc (i[xy]+o) 1558 | ; <$dd|$fd> $cb $oo $0e rrc (i[xy]+o) 1559 | ; <$dd|$fd> $cb $oo $16 rl (i[xy]+o) 1560 | ; <$dd|$fd> $cb $oo $1e rr (i[xy]+o) 1561 | ; <$dd|$fd> $cb $oo $26 sla (i[xy]+o) 1562 | ; <$dd|$fd> $cb $oo $2e sra (i[xy]+o) 1563 | ; <$dd|$fd> $cb $oo $36 sll (i[xy]+o) (undocumented) 1564 | ; <$dd|$fd> $cb $oo $3e srl (i[xy]+o) 1565 | ; ix+1 and iy+1 point to memop low 1566 | rotxy: 1567 | ; <----opcode----> 1568 | TestData $dd, $cb, 1, $06, $ddaf, MachineStateBeforeTest.memop-1, MachineStateBeforeTest.memop-1, $ff3c, $dbf6, $94f4, $82, $80, $61d9 1569 | TestData $20, 0, 0, $38, 0, 0, 0, 0, 0, 0, $80, 0, 0 ; 5 bits -> 32 permutations 1570 | TestData 0, 0, 0, 0, $00ff, 0, 0, 0, 0, 0, $57, 0, 0 ; 13 bits -> 14 permutations 1571 | CRCs $b40e85cb $b4347c81 1572 | .db FlagMask 1573 | MessageString "shf/rot (ix/y+1)" 1574 | 1575 | ; shift/rotate (6912 cases) 1576 | ; Opcodes: 1577 | ; $cb %00000sss rlc sss 1578 | ; $cb %00001sss rrc sss 1579 | ; $cb %00010sss rl sss 1580 | ; $cb %00011sss rr sss 1581 | ; $cb %00100sss sla sss 1582 | ; $cb %00101sss sra sss 1583 | ; $cb %00110sss sll sss 1584 | ; $cb %00111sss srl sss 1585 | ; sss = b|c|d|e|h|l|(hl)|a 1586 | rotz80: 1587 | ; <---opcode---> 1588 | TestData2 $cb, %00000000, $cceb, $5d4a, $e007, MachineStateBeforeTest.memop, $1395, $30ee, $43, $78, $3dad 1589 | TestData2 0, %00111111, 0, 0, 0, 0, 0, 0, $80, 0, 0 ; 7 bits -> 128 permutations 1590 | TestData2 0, 0, $00ff, 0, 0, 0, $ffff, $ffff, $57, $ff, 0 ; 53 bits -> 54 permutations 1591 | CRCs $ee0c828b $150c42ed 1592 | .db FlagMask 1593 | MessageString "shift/rotate r/(hl)" 1594 | 1595 | ; n, (7040 cases) 1596 | ; Opcodes: 1597 | ; $cb %10sssnnn res n, sss 1598 | ; $cb %11sssnnn set n, sss 1599 | ; sss = b|c|d|e|h|l|(hl)|a 1600 | srz80: 1601 | ; <---opcode---> 1602 | TestData2 $cb, %10000000, $2cd5, $97ab, $39ff, MachineStateBeforeTest.memop, $d14b, $6ab2, $53, $27, $b538 1603 | TestData2 0, %01111111, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 7 bits -> 128 permutations 1604 | TestData2 0, 0, $00ff, 0, 0, 0, $ffff, $ffff, FlagMask, $ff, 0 ; 54/56 bits -> 55/57 permutations 1605 | CRCs $90aa19cd $fdacf700 1606 | .db FlagMask 1607 | MessageString "set/res n,r/(hl)" 1608 | 1609 | ; n, (+1) (480 cases) 1610 | ; Opcodes: 1611 | ; <$dd|$fd> $cb $oo %10nnn110 res n, (i[xy]+o) 1612 | ; <$dd|$fd> $cb $oo %11nnn110 set n, (i[xy]+o) 1613 | srzx: 1614 | ; <--------opcode-------> 1615 | TestData $dd, $cb, +1, %10000110, $fb44, MachineStateBeforeTest.memop-1, MachineStateBeforeTest.memop-1, $ba09, $68be, $32d8, $10, $5e, $a867 1616 | TestData $20, 0, 0, %01111000, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 5 bits -> 32 permutations 1617 | TestData 0, 0, 0, 0, $00ff, 0, 0, 0, 0, 0, FlagMask, 0, 0 ; 14/16 bits -> 15/17 permutations 1618 | CRCs $177e3cb8 $dbf471a8 1619 | .db FlagMask 1620 | MessageString "set/res n,(ix/y+1)" 1621 | 1622 | ; ld (+1), (2112 cases) 1623 | ; Opcode: 1624 | ; <$dd|$fd> %011100ss $oo 1625 | ; ss = b|c|d|e 1626 | ; i[xy] point to memop or memop ^ 1, offsets 0 and 1 are exercised 1627 | ; Warning: there is a state leak if MachineStateBeforeTest is odd 1628 | st8ix1: 1629 | ; <------opcode----> 1630 | TestData3 $dd, %01110000, 0, $270d, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $b73a, $887b, $99ee, $86, $70, $ca07 1631 | TestData3 $20, %00000011, +1, 0, 1, 1, 0, 0, 0, 0, 0, 0 ; 6 bits -> 64 permutations 1632 | TestData3 0, 0, 0, 0, 0, 0, 0, $ffff, $ffff, 0, 0, 0 ; 32 bits -> 33 permutations 1633 | CRCs $24c66b95 $24c66b95 1634 | .db FlagMask 1635 | MessageString "ld (ix/y+1),b/c/d/e" 1636 | 1637 | ; ld (+1), (544 cases) 1638 | ; Opcode: 1639 | ; <$dd|$fd> %0111010s $oo 1640 | ; s = h|l 1641 | ; i[xy] point to memop or memop ^ 1, offsets 0 and 1 are exercised 1642 | ; Warning: there is a state leak if MachineStateBeforeTest is odd 1643 | st8ix2: 1644 | ; <------opcode----> 1645 | TestData3 $dd, %01110100, 0, $b664, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $e8ac, $b5f5, $aafe, $12, $10, $9566 1646 | TestData3 $20, %00000001, +1, 0, 1, 1, 0, 0, 0, 0, 0, 0 ; 5 bits -> 32 permutations 1647 | TestData3 0, 0, 0, 0, 0, 0, $ffff, 0, 0, 0, 0, 0 ; 16 bits -> 17 permutations 1648 | CRCs $b9b5243c $b9b5243c 1649 | .db FlagMask 1650 | MessageString "ld (ix/y+1),h/l" 1651 | 1652 | ; ld (+1), a (144 cases) 1653 | ; Opcode: 1654 | ; <$dd|$fd> %01110111 $oo 1655 | ; i[xy] point to memop or memop ^ 1, offsets 0 and 1 are exercised 1656 | ; Warning: there is a state leak if MachineStateBeforeTest is odd 1657 | st8ix3: 1658 | ; <--opcode--> 1659 | TestData3 $dd, $77, 0, $67af, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop, $4f13, $0644, $bcd7, $50, $ac, $5faf 1660 | TestData3 $20, 0, +1, 0, 1, 1, 0, 0, 0, 0, 0, 0 ; 4 bits -> 16 permutations 1661 | TestData3 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, $ff, 0 ; 8 bits -> 9 permutations 1662 | CRCs $51c0f862 $51c0f862 1663 | .db FlagMask 1664 | MessageString "ld (ix/y+1),a" 1665 | 1666 | ; ld (), a (100 cases) 1667 | ; Opcodes: 1668 | ; $02 ld (bc), a 1669 | ; $0a ld a, (bc) 1670 | ; $12 ld (de), a 1671 | ; $1a ld a, (de) 1672 | ; TODO: should change $18 to $10 as other opcodes are tested elsewhere 1673 | stabd: 1674 | ; 1675 | TestData1 $02, $0c3b, $b592, $6cff, $959e, MachineStateBeforeTest.memop, MachineStateBeforeTest.memop+1, $c1, $21, $bde7 1676 | TestData1 $18, 0, 0, 0, 0, 0, 0, 0, 0, 0 ; 4 bits -> 4 permutations 1677 | TestData1 0, $ffff, 0, 0, 0, 0, 0, 0, $ff, 0 ; 24 bits -> 25 permutations 1678 | CRCs $257d7a11 $257d7a11 1679 | .db FlagMask 1680 | MessageString "ld (bc/de),a" 1681 | .ends 1682 | 1683 | .section "Test runner" free 1684 | ; Starts test pointed to by (hl) 1685 | StartTest: 1686 | push hl 1687 | .ifdef WriteToScreen 1688 | ld a, -1 1689 | ld (ProgressCounter), a 1690 | .endif 1691 | ld a, (hl) ; get pointer to test 1692 | inc hl 1693 | ld h, (hl) 1694 | ld l, a 1695 | push hl 1696 | ld de, Test.CounterBits 1697 | add hl, de ; point to incmask 1698 | ld de, Counter.Buffer 1699 | call InitMask 1700 | pop hl 1701 | push hl 1702 | ld de, Test.ShifterBits 1703 | add hl, de ; point to scanmask 1704 | ld de, Shifter.Buffer 1705 | call InitMask 1706 | ld hl, Shifter.Buffer 1707 | ld (hl), 1 ; first bit 1708 | pop hl 1709 | push hl 1710 | ; Store the flags mask for this test 1711 | ld de, Test.FlagsMask 1712 | add hl, de 1713 | ld a, (hl) 1714 | ld (FlagsMaskForTest), a 1715 | pop hl 1716 | push hl 1717 | ; hl points to Test.BaseState.Opcode1 1718 | ld de, TestInRAM + OffsetOfInstructionUnderTest ; self-modify instruction to Test 1719 | ld bc, OpcodeCount 1720 | ldir 1721 | ; hl now points to Test.BaseState.MachineState 1722 | ld de, MachineStateBeforeTest ; copy initial machine state 1723 | ld bc, _sizeof_MachineState 1724 | ldir 1725 | ; hl now points to Test.CounterBits 1726 | ld de, Test.Name - Test.CounterBits ; skip counter and shifter data, and expected CRC 1727 | add hl, de 1728 | ex de, hl 1729 | call OutputText ; show Test name 1730 | ld a, ' ' 1731 | call PrintChar 1732 | .ifdef WriteToScreen 1733 | ; Show progress placeholder 1734 | ; We want to print this without using the console routines, 1735 | ; so it's screen-only. 1736 | pop hl ; Get pointer back 1737 | push hl 1738 | call ComputeTestCount 1739 | ex de, hl ; dave in de 1740 | ; Preserve the console state so we can print over the numbers 1741 | ld hl, (VRAMAddress) 1742 | push hl 1743 | ld a, (CursorX) 1744 | push af 1745 | ld a,1 1746 | ld (PrintOnlyToScreen), a 1747 | ld a, (IsSMSVDP) 1748 | or a 1749 | jr z,_textmode 1750 | _mode4: 1751 | ld a, l 1752 | and %11000000 ; Mask to row 1753 | add 64-20 ; Right side 1754 | ld l, a 1755 | jr + 1756 | _textmode: 1757 | ; Make hl be the end of the current row 1758 | ; Do this by subtracting CursorX and adding 31 1759 | push de 1760 | ld de, 31 1761 | add hl, de 1762 | ld a, (CursorX) 1763 | neg 1764 | ld e, a 1765 | ld d, -1 1766 | add hl, de 1767 | pop de 1768 | +: ld (VRAMAddress), hl 1769 | ld (VRAMAddressForCounter), hl 1770 | call SetVRAMAddress 1771 | xor a 1772 | ld (CursorX), a 1773 | call PrintByte 1774 | xor a 1775 | call PrintByte 1776 | ld a, '/' 1777 | call PrintChar 1778 | ld a, d 1779 | call PrintByte 1780 | ld a, e 1781 | call PrintByte 1782 | ld a, ' ' 1783 | call PrintChar 1784 | xor a 1785 | ld (PrintOnlyToScreen), a 1786 | pop af 1787 | ld (CursorX), a 1788 | pop hl 1789 | ld (VRAMAddress), hl 1790 | .endif 1791 | 1792 | call InitialiseCRC 1793 | ld hl, 0 1794 | ld (CaseCount), hl 1795 | 1796 | .define HALT_OPCODE $76 1797 | 1798 | TestLoop: 1799 | ld a, (TestInRAM + OffsetOfInstructionUnderTest) 1800 | cp HALT_OPCODE ; pragmatically avoid halt instructions 1801 | jr z, ++ 1802 | and $df ; check for prefix bytes 1803 | cp $dd 1804 | jp nz, + 1805 | ld a, (TestInRAM + OffsetOfInstructionUnderTest + 1) 1806 | cp HALT_OPCODE 1807 | +: call nz, TestInRAM ; execute the test instruction 1808 | .ifdef WriteToScreen 1809 | call UpdateProgressIndicator 1810 | .endif 1811 | ++: call count ; increment the counter 1812 | call nz, shift ; shift the scan bit if the counter is done 1813 | pop hl ; pointer to test case 1814 | jr nz, _done ; done if shift returned NZ 1815 | 1816 | push hl 1817 | ld a, 1 ; initialise count and shift scanners 1818 | ld (Counter.Bit), a 1819 | ld (Shifter.Bit), a 1820 | ld hl, Counter.Buffer 1821 | ld (Counter.Byte), hl 1822 | ld hl, Shifter.Buffer 1823 | ld (Shifter.Byte), hl 1824 | 1825 | ld b, 4 ; bytes in iut field 1826 | pop hl ; pointer to Test case 1827 | push hl 1828 | ld de, TestInRAM + OffsetOfInstructionUnderTest 1829 | call _SetupTestCase ; setup iut 1830 | ld b, _sizeof_MachineState 1831 | ld de, MachineStateBeforeTest 1832 | call _SetupTestCase ; setup machine state 1833 | jp TestLoop 1834 | 1835 | _done: 1836 | ld de, _sizeof_TestCase * 3 1837 | add hl, de ; point to expected crc 1838 | call CompareCRC 1839 | ; 1840 | ld de, Message_Pass 1841 | jp z, _Pass 1842 | push hl ; save pointer to crc 1843 | ld hl, CRCValue 1844 | ld de, Message_ActualCRC 1845 | call OutputText 1846 | call PrintHex32 1847 | ld de, Message_ExpectedCRC 1848 | call OutputText 1849 | pop hl ; get pointer to crc back 1850 | call PrintHex32 1851 | ld de, Message_NewLine 1852 | _Pass: 1853 | call OutputText 1854 | pop hl 1855 | inc hl 1856 | inc hl 1857 | ret ; end of test 1858 | 1859 | ; set up a field of the test case 1860 | ; b = number of bytes 1861 | ; hl = pointer to base case 1862 | ; de = destination 1863 | _SetupTestCase: 1864 | ;--: 1865 | push bc 1866 | push de 1867 | push hl 1868 | ld c, (hl) ; get base byte 1869 | ld de, _sizeof_TestCase 1870 | add hl, de ; point to counter mask 1871 | ld a, (hl) 1872 | or a 1873 | jp z, + ; Skip if 0 1874 | 1875 | ld b, 8 ; 8 bits 1876 | -: rrca 1877 | push af 1878 | ld a, 0 1879 | call c, GetNextCounterBit ; get next counter bit if mask bit was set 1880 | xor c ; flip bit if counter bit was set 1881 | rrca 1882 | ld c, a 1883 | pop af 1884 | djnz - 1885 | 1886 | +: ld de, _sizeof_TestCase 1887 | add hl, de ; point to shifter mask 1888 | ld a, (hl) 1889 | or a 1890 | jp z, + ; Skip if 0 1891 | 1892 | ld b, 8 ; 8 bits 1893 | -: rrca 1894 | push af 1895 | ld a, 0 1896 | call c, GetNextShifterBit ; get next shifter bit if mask bit was set 1897 | xor c ; flip bit if shifter bit was set 1898 | rrca 1899 | ld c, a 1900 | pop af 1901 | djnz - 1902 | 1903 | +: pop hl 1904 | pop de 1905 | ld a, c 1906 | ld (de), a ; mangled byte to destination 1907 | inc de 1908 | pop bc 1909 | inc hl 1910 | djnz _SetupTestCase 1911 | ret 1912 | 1913 | ; get next counter bit in low bit of a 1914 | GetNextCounterBit: 1915 | push bc 1916 | push hl 1917 | ld hl, (Counter.Byte) 1918 | ld b, (hl) 1919 | ld hl, Counter.Bit 1920 | ld a, (hl) 1921 | ld c, a 1922 | rlca 1923 | ld (hl), a 1924 | cp 1 1925 | jp nz, + 1926 | ld hl, (Counter.Byte) 1927 | inc hl 1928 | ld (Counter.Byte), hl 1929 | +: ld a, b 1930 | and c 1931 | pop hl 1932 | pop bc 1933 | ret z 1934 | ld a, 1 1935 | ret 1936 | 1937 | ; get next shifter bit in low bit of a 1938 | GetNextShifterBit: 1939 | push bc 1940 | push hl 1941 | ld hl, (Shifter.Byte) 1942 | ld b, (hl) 1943 | ld hl, Shifter.Bit 1944 | ld a, (hl) 1945 | ld c, a 1946 | rlca 1947 | ld (hl), a 1948 | cp 1 1949 | jp nz, + 1950 | ld hl, (Shifter.Byte) 1951 | inc hl 1952 | ld (Shifter.Byte), hl 1953 | +: ld a, b 1954 | and c 1955 | pop hl 1956 | pop bc 1957 | ret z 1958 | ld a, 1 1959 | ret 1960 | 1961 | ; clear memory at hl, bc bytes 1962 | clrmem: 1963 | 1964 | ; initialise Counter or Shifter 1965 | ; de = pointer to work area for Counter or Shifter 1966 | ; hl = pointer to mask 1967 | InitMask: 1968 | push de 1969 | ex de, hl 1970 | ld bc, 20+20 1971 | ; clear work area 1972 | push de 1973 | push hl 1974 | ld (hl), 0 1975 | ld d, h 1976 | ld e, l 1977 | inc de 1978 | dec bc 1979 | ldir 1980 | pop hl 1981 | pop de 1982 | ex de, hl 1983 | ld b, 20 ; byte counter 1984 | ld c, 1 ; first bit 1985 | ld d, 0 ; bit Counter 1986 | --:ld e, (hl) 1987 | -:ld a, e 1988 | and c 1989 | jp z, + 1990 | inc d 1991 | +: ld a, c 1992 | rlca 1993 | ld c, a 1994 | cp 1 1995 | jp nz, - 1996 | inc hl 1997 | djnz -- 1998 | ; got number of 1-bits in mask in reg d 1999 | ld a, d 2000 | and $f8 2001 | rrca 2002 | rrca 2003 | rrca ; divide by 8 (get byte offset) 2004 | ld l, a 2005 | ld h, 0 2006 | ld a, d 2007 | and 7 ; bit offset 2008 | inc a 2009 | ld b, a 2010 | ld a, $80 2011 | -:rlca 2012 | djnz - 2013 | pop de 2014 | add hl, de 2015 | ld de, 20 2016 | add hl, de 2017 | ld (hl), a 2018 | ret 2019 | 2020 | ; multi-byte counter 2021 | count: 2022 | push bc 2023 | push de 2024 | push hl 2025 | ld hl, Counter.Buffer ; 20 byte counter starts here 2026 | ld de, _sizeof_TestCase ; somewhere in here is the stop bit 2027 | ex de, hl 2028 | add hl, de 2029 | ex de, hl 2030 | -:inc (hl) 2031 | ld a, (hl) 2032 | or a 2033 | jp z, ++ ; overflow to next byte 2034 | ld b, a 2035 | ld a, (de) 2036 | and b ; Test for terminal value 2037 | jp z, + 2038 | ld (hl), 0 ; reset to zero 2039 | +:pop bc 2040 | pop de 2041 | pop hl 2042 | ret 2043 | 2044 | ++: inc hl 2045 | inc de 2046 | jp - 2047 | 2048 | ; multi-byte shifter 2049 | shift: 2050 | push bc 2051 | push de 2052 | push hl 2053 | ld hl, Shifter.Buffer ; 20 byte shift register starts here 2054 | ld de, _sizeof_TestCase ; somewhere in here is the stop bit 2055 | ex de, hl 2056 | add hl, de 2057 | ex de, hl 2058 | -:ld a, (hl) 2059 | or a 2060 | jp z, ++ 2061 | ld b, a 2062 | ld a, (de) 2063 | and b 2064 | jp nz, + 2065 | ld a, b 2066 | rlca 2067 | cp 1 2068 | jp nz, +++ 2069 | ld (hl), 0 2070 | inc hl 2071 | inc de 2072 | +++:ld (hl), a 2073 | xor a ; set Z 2074 | +:pop hl 2075 | pop de 2076 | pop bc 2077 | ret 2078 | 2079 | ++: inc hl 2080 | inc de 2081 | jp - 2082 | .ends 2083 | 2084 | .section "Test harness" free 2085 | ; Self-modifying so it should be in RAM 2086 | .block "Test code" 2087 | TestCode: 2088 | push af 2089 | push bc 2090 | push de 2091 | push hl 2092 | ld (StackPointerSaved), sp ; save stack pointer 2093 | ld sp, MachineStateBeforeTest.iy ; point to test-case machine state 2094 | pop iy ; and load all regs 2095 | pop ix 2096 | pop hl 2097 | pop de 2098 | pop bc 2099 | pop af 2100 | ld sp, (MachineStateBeforeTest.sp) 2101 | 2102 | @InstructionUnderTest: 2103 | .dsb 4, 0 ; max 4 byte instruction under Test, modified at runtime 2104 | 2105 | ld (MachineStateAfterTest.sp), sp ; save stack pointer 2106 | ld sp, MachineStateAfterTest.sp 2107 | push af ; save other registers 2108 | push bc 2109 | push de 2110 | push hl 2111 | push ix 2112 | push iy 2113 | ld sp, (StackPointerSaved) ; restore stack pointer 2114 | ld hl, (MachineStateBeforeTest.memop) ; overwrite memop with initial value 2115 | ld (MachineStateAfterTest.memop), hl 2116 | 2117 | ; Mask out undocumented flags 2118 | ld a, (FlagsMaskForTest) 2119 | ld hl, MachineStateAfterTest.f ; flags after Test 2120 | and (hl) ; mask-out irrelevant bits 2121 | ld (hl), a 2122 | 2123 | ; Update CRC - speed optimised version 2124 | ; Based on code by asynchronous: https://www.smspower.org/forums/18523-BitBangingAndCartridgeDumping 2125 | ; Later changed based on z80test: https://github.com/raxoft/z80test 2126 | ld hl, MachineStateAfterTest 2127 | ld b, _sizeof_MachineState 2128 | ; get current CRC into b'c'd'e' 2129 | exx 2130 | ld hl, CRCValue 2131 | push hl ; for later 2132 | ld b,(hl) 2133 | inc hl 2134 | ld c,(hl) 2135 | inc hl 2136 | ld d,(hl) 2137 | inc hl 2138 | ld e,(hl) 2139 | exx 2140 | -: ld a,(hl) ; Get the next byte to be CRC'ed 2141 | exx 2142 | xor e 2143 | ld l, a 2144 | ld h, >crctable 2145 | ld a, (hl) 2146 | xor d 2147 | ld e, a 2148 | inc h 2149 | ld a, (hl) 2150 | xor c 2151 | ld d, a 2152 | inc h 2153 | ld a, (hl) 2154 | xor b 2155 | ld c, a 2156 | inc h 2157 | ld b, (hl) 2158 | exx 2159 | inc hl 2160 | djnz - 2161 | exx 2162 | ; Save intermediate result in RAM 2163 | pop hl ; CRCValue 2164 | ld (hl), b 2165 | inc hl 2166 | ld (hl), c 2167 | inc hl 2168 | ld (hl), d 2169 | inc hl 2170 | ld (hl), e 2171 | exx 2172 | pop hl 2173 | pop de 2174 | pop bc 2175 | pop af 2176 | ret 2177 | .endb 2178 | .define OffsetOfInstructionUnderTest TestCode@InstructionUnderTest - TestCode 2179 | .export OffsetOfInstructionUnderTest 2180 | .ends 2181 | 2182 | .section "Text display" free 2183 | ; display hex 2184 | ; display the big-endian 32-bit value pointed to by hl 2185 | PrintHex32: 2186 | push af 2187 | push bc 2188 | push hl 2189 | ld b, 4 2190 | -:push bc 2191 | ld a, (hl) 2192 | call PrintByte 2193 | inc hl 2194 | pop bc 2195 | djnz - 2196 | pop hl 2197 | pop bc 2198 | pop af 2199 | ret 2200 | 2201 | ; display byte in a 2202 | PrintByte: 2203 | push af 2204 | .rept 4 2205 | rrca 2206 | .endr 2207 | call PrintNibble 2208 | pop af 2209 | ; fall through 2210 | 2211 | ; display low nibble in a 2212 | PrintNibble: 2213 | +:push bc 2214 | push hl 2215 | and $0f 2216 | cp 10 2217 | jp c, + 2218 | add a, 'a'-'9'-1 2219 | +: add a, '0' 2220 | call PrintChar 2221 | pop hl 2222 | pop bc 2223 | ret 2224 | 2225 | OutputText: 2226 | push af 2227 | push bc 2228 | push de 2229 | push hl 2230 | -: ld a, (de) 2231 | cp STREND 2232 | jr z, + 2233 | call PrintChar 2234 | inc de 2235 | jr - 2236 | +:pop hl 2237 | pop de 2238 | pop bc 2239 | pop af 2240 | ret 2241 | 2242 | PrintChar: 2243 | push hl 2244 | .ifdef WriteToScreen 2245 | call PrintChar_SMS 2246 | .endif 2247 | push af 2248 | ld a, (PrintOnlyToScreen) 2249 | or a 2250 | jr nz, + 2251 | pop af 2252 | .ifdef WriteToSDSCDebugConsole 2253 | call PrintChar_SDSC 2254 | .endif 2255 | .ifdef WriteToEmuliciousDebugConsole 2256 | call PrintChar_Emulicious 2257 | .endif 2258 | .ifdef WriteToSRAM 2259 | call PrintChar_SRAM 2260 | .endif 2261 | pop hl 2262 | ret 2263 | 2264 | +: pop af 2265 | pop hl 2266 | ret 2267 | 2268 | ; Messages 2269 | Message_TitleInfo: 2270 | .ifdef UndocumentedFlags 2271 | .asc "Undocumented" 2272 | .else 2273 | .asc "Documented" 2274 | .endif 2275 | .asc " flags version", NEWLINE, 2276 | .asc "Outputs:", NEWLINE, STREND 2277 | 2278 | Message_SDSCMode: 2279 | .asc "* SDSC Debug Console", NEWLINE, STREND 2280 | 2281 | Message_EmuliciousMode: 2282 | .asc "* Emulicious Debug Console", NEWLINE, STREND 2283 | 2284 | Message_SRAMMode: 2285 | .asc "* SRAM", NEWLINE, STREND 2286 | 2287 | Message_SMSMode: 2288 | .asc "* SMS Mode 4", NEWLINE, STREND 2289 | 2290 | Message_TMSMode: 2291 | .asc "* TMS9918 Text Mode", NEWLINE, STREND 2292 | 2293 | Message_Done: 2294 | .asc "Tests complete", NEWLINE, STREND 2295 | Message_Pass: 2296 | .asc "OK", NEWLINE, STREND 2297 | Message_ActualCRC: 2298 | .asc NEWLINE, " CRC ", STREND 2299 | Message_ExpectedCRC: 2300 | .asc " expected ", STREND 2301 | Message_NewLine: 2302 | .asc NEWLINE, STREND 2303 | 2304 | Message_SCF_CCF: 2305 | .asc "SCF/CCF tests:", NEWLINE, STREND 2306 | .ends 2307 | 2308 | .section "CRC code" free 2309 | ; compare crc 2310 | ; hl points to value to compare to CRCValue 2311 | CompareCRC: 2312 | push bc 2313 | push de 2314 | push hl 2315 | ld de, CRCValue 2316 | ld b, 4 2317 | -:ld a, (de) 2318 | cp (hl) 2319 | jp nz, + 2320 | inc hl 2321 | inc de 2322 | djnz - 2323 | +:pop hl 2324 | pop de 2325 | pop bc 2326 | ret 2327 | 2328 | InitialiseCRC: 2329 | push af 2330 | push bc 2331 | push hl 2332 | ld hl, CRCValue 2333 | ld a, $ff 2334 | ld b, 4 2335 | -:ld (hl), a 2336 | inc hl 2337 | djnz - 2338 | pop hl 2339 | pop bc 2340 | pop af 2341 | ret 2342 | .ends 2343 | 2344 | 2345 | .section "CRC lookup table" align 256 2346 | crctable: 2347 | ; This is the normal CRC lookup table, except it is deinterleaved by 4: the first 256 bytes are the first (most significant) byte of each value, the next 256 are the second, etc. This allows faster lookup. 2348 | .db $00 $96 $2c $ba $19 $8f $35 $a3 $32 $a4 $1e $88 $2b $bd $07 $91 $64 $f2 $48 $de $7d $eb $51 $c7 $56 $c0 $7a $ec $4f $d9 $63 $f5 $c8 $5e $e4 $72 $d1 $47 $fd $6b $fa $6c $d6 $40 $e3 $75 $cf $59 $ac $3a $80 $16 $b5 $23 $99 $0f $9e $08 $b2 $24 $87 $11 $ab $3d $90 $06 $bc $2a $89 $1f $a5 $33 $a2 $34 $8e $18 $bb $2d $97 $01 $f4 $62 $d8 $4e $ed $7b $c1 $57 $c6 $50 $ea $7c $df $49 $f3 $65 $58 $ce $74 $e2 $41 $d7 $6d $fb $6a $fc $46 $d0 $73 $e5 $5f $c9 $3c $aa $10 $86 $25 $b3 $09 $9f $0e $98 $22 $b4 $17 $81 $3b $ad $20 $b6 $0c $9a $39 $af $15 $83 $12 $84 $3e $a8 $0b $9d $27 $b1 $44 $d2 $68 $fe $5d $cb $71 $e7 $76 $e0 $5a $cc $6f $f9 $43 $d5 $e8 $7e $c4 $52 $f1 $67 $dd $4b $da $4c $f6 $60 $c3 $55 $ef $79 $8c $1a $a0 $36 $95 $03 $b9 $2f $be $28 $92 $04 $a7 $31 $8b $1d $b0 $26 $9c $0a $a9 $3f $85 $13 $82 $14 $ae $38 $9b $0d $b7 $21 $d4 $42 $f8 $6e $cd $5b $e1 $77 $e6 $70 $ca $5c $ff $69 $d3 $45 $78 $ee $54 $c2 $61 $f7 $4d $db $4a $dc $66 $f0 $53 $c5 $7f $e9 $1c $8a $30 $a6 $05 $93 $29 $bf $2e $b8 $02 $94 $37 $a1 $1b $8d 2349 | .db $00 $30 $61 $51 $c4 $f4 $a5 $95 $88 $b8 $e9 $d9 $4c $7c $2d $1d $10 $20 $71 $41 $d4 $e4 $b5 $85 $98 $a8 $f9 $c9 $5c $6c $3d $0d $20 $10 $41 $71 $e4 $d4 $85 $b5 $a8 $98 $c9 $f9 $6c $5c $0d $3d $30 $00 $51 $61 $f4 $c4 $95 $a5 $b8 $88 $d9 $e9 $7c $4c $1d $2d $41 $71 $20 $10 $85 $b5 $e4 $d4 $c9 $f9 $a8 $98 $0d $3d $6c $5c $51 $61 $30 $00 $95 $a5 $f4 $c4 $d9 $e9 $b8 $88 $1d $2d $7c $4c $61 $51 $00 $30 $a5 $95 $c4 $f4 $e9 $d9 $88 $b8 $2d $1d $4c $7c $71 $41 $10 $20 $b5 $85 $d4 $e4 $f9 $c9 $98 $a8 $3d $0d $5c $6c $83 $b3 $e2 $d2 $47 $77 $26 $16 $0b $3b $6a $5a $cf $ff $ae $9e $93 $a3 $f2 $c2 $57 $67 $36 $06 $1b $2b $7a $4a $df $ef $be $8e $a3 $93 $c2 $f2 $67 $57 $06 $36 $2b $1b $4a $7a $ef $df $8e $be $b3 $83 $d2 $e2 $77 $47 $16 $26 $3b $0b $5a $6a $ff $cf $9e $ae $c2 $f2 $a3 $93 $06 $36 $67 $57 $4a $7a $2b $1b $8e $be $ef $df $d2 $e2 $b3 $83 $16 $26 $77 $47 $5a $6a $3b $0b $9e $ae $ff $cf $e2 $d2 $83 $b3 $26 $16 $47 $77 $6a $5a $0b $3b $ae $9e $cf $ff $f2 $c2 $93 $a3 $36 $06 $57 $67 $7a $4a $1b $2b $be $8e $df $ef 2350 | .db $00 $07 $0e $09 $6d $6a $63 $64 $db $dc $d5 $d2 $b6 $b1 $b8 $bf $b7 $b0 $b9 $be $da $dd $d4 $d3 $6c $6b $62 $65 $01 $06 $0f $08 $6e $69 $60 $67 $03 $04 $0d $0a $b5 $b2 $bb $bc $d8 $df $d6 $d1 $d9 $de $d7 $d0 $b4 $b3 $ba $bd $02 $05 $0c $0b $6f $68 $61 $66 $dc $db $d2 $d5 $b1 $b6 $bf $b8 $07 $00 $09 $0e $6a $6d $64 $63 $6b $6c $65 $62 $06 $01 $08 $0f $b0 $b7 $be $b9 $dd $da $d3 $d4 $b2 $b5 $bc $bb $df $d8 $d1 $d6 $69 $6e $67 $60 $04 $03 $0a $0d $05 $02 $0b $0c $68 $6f $66 $61 $de $d9 $d0 $d7 $b3 $b4 $bd $ba $b8 $bf $b6 $b1 $d5 $d2 $db $dc $63 $64 $6d $6a $0e $09 $00 $07 $0f $08 $01 $06 $62 $65 $6c $6b $d4 $d3 $da $dd $b9 $be $b7 $b0 $d6 $d1 $d8 $df $bb $bc $b5 $b2 $0d $0a $03 $04 $60 $67 $6e $69 $61 $66 $6f $68 $0c $0b $02 $05 $ba $bd $b4 $b3 $d7 $d0 $d9 $de $64 $63 $6a $6d $09 $0e $07 $00 $bf $b8 $b1 $b6 $d2 $d5 $dc $db $d3 $d4 $dd $da $be $b9 $b0 $b7 $08 $0f $06 $01 $65 $62 $6b $6c $0a $0d $04 $03 $67 $60 $69 $6e $d1 $d6 $df $d8 $bc $bb $b2 $b5 $bd $ba $b3 $b4 $d0 $d7 $de $d9 $66 $61 $68 $6f $0b $0c $05 $02 2351 | .db $00 $77 $ee $99 $07 $70 $e9 $9e $0e $79 $e0 $97 $09 $7e $e7 $90 $1d $6a $f3 $84 $1a $6d $f4 $83 $13 $64 $fd $8a $14 $63 $fa $8d $3b $4c $d5 $a2 $3c $4b $d2 $a5 $35 $42 $db $ac $32 $45 $dc $ab $26 $51 $c8 $bf $21 $56 $cf $b8 $28 $5f $c6 $b1 $2f $58 $c1 $b6 $76 $01 $98 $ef $71 $06 $9f $e8 $78 $0f $96 $e1 $7f $08 $91 $e6 $6b $1c $85 $f2 $6c $1b $82 $f5 $65 $12 $8b $fc $62 $15 $8c $fb $4d $3a $a3 $d4 $4a $3d $a4 $d3 $43 $34 $ad $da $44 $33 $aa $dd $50 $27 $be $c9 $57 $20 $b9 $ce $5e $29 $b0 $c7 $59 $2e $b7 $c0 $ed $9a $03 $74 $ea $9d $04 $73 $e3 $94 $0d $7a $e4 $93 $0a $7d $f0 $87 $1e $69 $f7 $80 $19 $6e $fe $89 $10 $67 $f9 $8e $17 $60 $d6 $a1 $38 $4f $d1 $a6 $3f $48 $d8 $af $36 $41 $df $a8 $31 $46 $cb $bc $25 $52 $cc $bb $22 $55 $c5 $b2 $2b $5c $c2 $b5 $2c $5b $9b $ec $75 $02 $9c $eb $72 $05 $95 $e2 $7b $0c $92 $e5 $7c $0b $86 $f1 $68 $1f $81 $f6 $6f $18 $88 $ff $66 $11 $8f $f8 $61 $16 $a0 $d7 $4e $39 $a7 $d0 $49 $3e $ae $d9 $40 $37 $a9 $de $47 $30 $bd $ca $53 $24 $ba $cd $54 $23 $b3 $c4 $5d $2a $b4 $c3 $5a $2d 2352 | .ends 2353 | 2354 | .section "SDSC console" free 2355 | .include "sdsc.inc" 2356 | 2357 | ; (erq) Re-write SMSInitialise to remove VDP-specific actions, and 2358 | ; (erq) replace with debug console initialization code. 2359 | 2360 | Initialise_SDSC: 2361 | ; Disable joystick ports. This enables ports in region $C0 through $FF 2362 | ; allowing Debug Console ports at $FC and $FD to be visible. 2363 | ; We need to write to port $3e but we also need to detect what port we are in... 2364 | call DetectPort3EValue 2365 | ; Disable the cotroller ports 2366 | ld a, (Port3EValue) 2367 | or %00000100 2368 | out ($3e), a 2369 | 2370 | ; Clear Debug Console screen 2371 | ld a, SDSC_DEBUGCONSOLE_COMMAND_CLEARSCREEN 2372 | out (SDSC_OUTPORT_DEBUGCONSOLE_COMMAND), a 2373 | 2374 | ret 2375 | 2376 | DetectPort3EValue: 2377 | ; Copy some code to RAM 2378 | ld hl, DetectPort3EValue_Code 2379 | ld de, TestInRAM ; using the same area as for tests 2380 | ld bc, _sizeof_DetectPort3EValue_Code 2381 | ldir 2382 | jp TestInRAM 2383 | 2384 | DetectPort3EValue_Code: 2385 | ; We write some values to port $3E and check if we find ourself there 2386 | ld hl, @ValuesToTry - DetectPort3EValue_Code + TestInRAM ; Pointer after loading to RAM 2387 | --: 2388 | ld a, (hl) 2389 | or a 2390 | jr z, @AllFailed 2391 | out ($3e), a 2392 | push af 2393 | push hl 2394 | ; Check if it matches 2395 | ld hl, DetectPort3EValue_Code 2396 | ld de, TestInRAM 2397 | ld b, _sizeof_DetectPort3EValue_Code 2398 | -:ld a, (de) 2399 | cp (hl) 2400 | inc hl 2401 | inc de 2402 | jr nz, @Fail 2403 | djnz - 2404 | pop hl 2405 | pop af 2406 | ; success 2407 | ld (Port3EValue), a 2408 | ret 2409 | 2410 | @Fail: 2411 | pop hl 2412 | pop af 2413 | inc hl 2414 | jr -- 2415 | 2416 | @AllFailed: 2417 | ; Emulator? Let's put a default there 2418 | ld a, %10101011 ; RAM + IO + cart 2419 | ld (Port3EValue), a 2420 | out ($3e), a 2421 | ret 2422 | 2423 | @ValuesToTry: 2424 | .db %01101011 ; RAM + IO + expansion 2425 | .db %10101011 ; RAM + IO + cart 2426 | .db %11001011 ; RAM + IO + card 2427 | .db %11100011 ; RAM + IO + BIOS (!) 2428 | ; ||||||``- No effect 2429 | ; |||||`--- I/O 2430 | ; ||||`---- BIOS 2431 | ; |||`----- RAM 2432 | ; ||`------ Card 2433 | ; |`------- Cart 2434 | ; `-------- Expansion 2435 | 2436 | .db 0 ; terminator 2437 | 2438 | PrintChar_SDSC: 2439 | push af 2440 | cp $0d ; Change \n to \r 2441 | jr nz, + 2442 | ld a, $0a 2443 | +: out (SDSC_OUTPORT_DEBUGCONSOLE_DATA), a 2444 | pop af 2445 | ret 2446 | .ends 2447 | 2448 | .section "Emulicious debug console" free 2449 | PrintChar_Emulicious: 2450 | push hl 2451 | push de 2452 | ld hl, _format 2453 | ld de, EmuliciousConsoleChar 2454 | cp $0d ; Change \n to \r 2455 | jr nz, + 2456 | ld a, $0a 2457 | +: ld (EmuliciousConsoleChar), a 2458 | ; Invoke a printf with the char 2459 | ld d, d 2460 | jr + 2461 | .dw $6464, $0200 2462 | +:pop de 2463 | pop hl 2464 | ret 2465 | _format: .db "%c", 0 2466 | .ends 2467 | 2468 | .ifdef WriteToScreen 2469 | .ramsection "SMS drawing routines variables" slot 3 2470 | CursorX db ; X coordinate of cursor 2471 | VRAMAddress dw ; VRAM address, pre-masked as a write command 2472 | VRAMAddressForCounter dw 2473 | Scroll db ; Current scrolling offset 2474 | ScrollFlag db ; Zero before we start scrolling, then 1 2475 | ProgressCounter db ; Counter for progress updates 2476 | NewlineAdded db ; Flag for handling inserted newlines before newlines 2477 | CaseCount dw ; Number of cases computed so far (divided by 64) 2478 | .ends 2479 | .endif 2480 | 2481 | .section "Font" free 2482 | font_8x8: 2483 | .incbin "ZX_Eurostile.1bpp" 2484 | 2485 | font_6x8: 2486 | .incbin "Envious.1bpp" 2487 | .ends 2488 | 2489 | .section "VDP initialisation" free 2490 | 2491 | .define SpriteSet 0 ; 0 for sprites to use tiles 0-255, 1 for 256+ 2492 | .define NameTableAddress $3800 ; must be a multiple of $800; usually $3800; fills $700 bytes (unstretched) 2493 | .define SpriteTableAddress $3f00 ; must be a multiple of $100; usually $3f00; fills $100 bytes 2494 | VDPRegisterInitialisation: 2495 | .db %00100100,$80 2496 | ; |||||||`- Disable sync 2497 | ; ||||||`-- Enable extra height modes 2498 | ; |||||`--- SMS mode instead of SG 2499 | ; ||||`---- Shift sprites left 8 pixels 2500 | ; |||`----- Enable line interrupts 2501 | ; ||`------ Blank leftmost column for scrolling 2502 | ; |`------- Fix top 2 rows during scrolling 2503 | ; `-------- Fix right 8 columns during scrolling 2504 | .db %10000000,$81 2505 | ; |||||||`- Zoomed sprites -> 16x16 pixels 2506 | ; ||||||`-- Doubled sprites -> 2 tiles per sprite, 8x16 2507 | ; |||||`--- Always 0 for Mega Drive compatibility 2508 | ; ||||`---- 30 row/240 line mode 2509 | ; |||`----- 28 row/224 line mode 2510 | ; ||`------ VBlank interrupts 2511 | ; |`------- Enable display 2512 | ; `-------- VRAM size bit - always 1 2513 | .db (NameTableAddress>>10)|%11110001,$82 ; Mask bits 0, 4-7 to 1 for compatibility 2514 | .db %11111111,$83 ; Unused, set all bits for compatibility 2515 | .db %11111111,$84 ; Unused, set all bits for compatibility 2516 | .db (SpriteTableAddress>>7)|%10000001,$85 ; Mask bits 0, 7 to 1 for compatibility 2517 | .db (SpriteSet<<2)|%10000011,$86 ; Mask bits 0-1, 7 for compatibility 2518 | .db $0,$87 2519 | ; `-------- Border palette colour (sprite palette) 2520 | .db $08,$88 2521 | ; ``------- Horizontal scroll 2522 | .db $00,$89 2523 | ; ``------- Vertical scroll 2524 | .db $ff,$8a 2525 | ; ``------- Line interrupt spacing 2526 | 2527 | VDPRegisterInitialisation_TMS: 2528 | .db %00000000,$80 2529 | ; |||||||`- EXTVID - Enables external video input 2530 | ; ||||||`-- M2 - Select screen mode 2531 | ; ``````--- Unused 2532 | .db %11010000,$81 2533 | ; |||||||`- MAG - Sprites enlarged if set (sprite pixels are 2x2) 2534 | ; ||||||`-- SI - 16x16 sprites if set; 8x8 if reset 2535 | ; |||||`--- Unused 2536 | ; ||||`---- M3 - Select screen mode 2537 | ; |||`----- M1 - Select screen mode 2538 | ; ||`------ GINT - Generate interrupts if set 2539 | ; |`------- BL - Blank screen if reset; just backdrop. Sprite system inactive 2540 | ; `-------- 4/16K - Selects 16kB RAM if set.(must be 1) 2541 | .db (NameTableAddress>>10)|%11110000,$82 2542 | .db $ff, $83 2543 | ; ``------- Colour table unused in text mode 2544 | .db 0,$84 2545 | ; `--------- Patterns start at address 0 like in SMS mode 2546 | .db $ff, $85 2547 | ; ``------- Sprite attribute table unused in text mode 2548 | .db $ff, $86 2549 | ; ``------- Sprite generator table unused in text mode 2550 | .db %11110001,$87 2551 | ; ||||````- Background colour (black) 2552 | ; ````----- Text colour (white) 2553 | 2554 | Palette: 2555 | .db $00,$30,$0c,$03,$3c,$33,$0f,$16,$19,$06,$35,$21,$0d,$37,$23,$3f 2556 | .db $00,$30,$0c,$03,$3c,$33,$0f,$16,$19,$06,$35,$21,$0d,$37,$23,$07 2557 | 2558 | .define VDP_ADDRESS $bf 2559 | .define VDP_REGISTER $bf 2560 | .define VDP_STATUS $bf 2561 | .define VDP_DATA $be 2562 | .define VRAM_WRITE_MASK $4000 2563 | 2564 | .macro SET_VDP_REGISTER args index, value 2565 | ld a, value 2566 | out (VDP_REGISTER), a 2567 | ld a, $80 | index 2568 | out (VDP_REGISTER), a 2569 | .endm 2570 | 2571 | .macro SET_VRAM_ADDRESS args value 2572 | ld a, value) 2575 | out (VDP_ADDRESS), a 2576 | .endm 2577 | 2578 | .macro SET_CRAM_ADDRESS args value 2579 | ld a, value) 2582 | out (VDP_ADDRESS), a 2583 | .endm 2584 | 2585 | Initialise_Screen: 2586 | push af 2587 | push bc 2588 | push de 2589 | push hl 2590 | call DetectSystem 2591 | 2592 | ld a, (IsSMSVDP) 2593 | or a 2594 | jp z, _TMS 2595 | _SMS: 2596 | call SetUpVDP 2597 | call ClearVRAM 2598 | call LoadFont 2599 | call LoadPalette 2600 | jr + 2601 | 2602 | _TMS: 2603 | call SetUpVDP_TMS 2604 | call ClearVRAM 2605 | call LoadFont_TMS 2606 | 2607 | +: 2608 | call InitCursor 2609 | 2610 | ; Turn screen on 2611 | ld a, (IsSMSVDP) 2612 | or a 2613 | jr z, + 2614 | SET_VDP_REGISTER 1, %11000000 2615 | ; ||||| |`- Zoomed sprites -> 16x16 pixels 2616 | ; ||||| `-- Doubled sprites -> 2 tiles per sprite, 8x16 2617 | ; ||||`---- 30 row/240 line mode 2618 | ; |||`----- 28 row/224 line mode 2619 | ; ||`------ VBlank interrupts 2620 | ; |`------- Enable display 2621 | ; `-------- Must be set (VRAM size bit) 2622 | jr ++ 2623 | +: SET_VDP_REGISTER 1, %11010000 2624 | ; |||||||`- MAG - Sprites enlarged if set (sprite pixels are 2x2) 2625 | ; ||||||`-- SI - 16x16 sprites if set; 8x8 if reset 2626 | ; |||||`--- Unused 2627 | ; ||||`---- M3 - Select screen mode 2628 | ; |||`----- M1 - Select screen mode 2629 | ; ||`------ GINT - Generate interrupts if set 2630 | ; |`------- BL - Blank screen if reset; just backdrop. Sprite system inactive 2631 | ; `-------- 4/16K - Selects 16kB RAM if set.(must be 1) 2632 | ++: 2633 | pop hl 2634 | pop de 2635 | pop bc 2636 | pop af 2637 | ret 2638 | 2639 | DetectSystem: 2640 | ; We allow controller overrides 2641 | ; Up = SMS 2642 | ; Down = TMS 2643 | in a, ($dc) 2644 | bit 0, a 2645 | jr z, _IsMode4 2646 | bit 1, a 2647 | jr z, _NotMode4 2648 | 2649 | ; We set the video to SMS mode with the sprite table at $3f00. 2650 | ; A TMS9918a will interpret this as mode 0 with the sprite attribute table at $3f80 (and sprite generator table at $1800, although it doesn't matter here). 2651 | call SetUpVDP 2652 | call ClearVRAM 2653 | ; Screen is off so we can write fast... 2654 | ; We set up the sprite table for five sprites on top of each other (for mode 4) 2655 | ld hl, VRAM_WRITE_MASK + 32 2656 | call SetVRAMAddress 2657 | ld b, 32 ; 32 bytes = 1 tile 2658 | ld a, $ff 2659 | -:out (VDP_DATA), a 2660 | djnz - 2661 | ; ys 2662 | ld hl, SpriteTableAddress | VRAM_WRITE_MASK 2663 | call SetVRAMAddress 2664 | xor a ; y = 0 2665 | ld b, 5 2666 | -:out (VDP_DATA), a 2667 | djnz - 2668 | ld a, 208 ; terminator 2669 | out (VDP_DATA), a 2670 | ; xns 2671 | ld hl, SpriteTableAddress | VRAM_WRITE_MASK + 128 2672 | call SetVRAMAddress 2673 | ld b, 5 2674 | -:ld a, 208 ; x = 208 2675 | out (VDP_DATA), a 2676 | ld a, 1 ; n = 1 2677 | out (VDP_DATA), a 2678 | djnz - 2679 | ; A TMS9918a will see the "xns" as the sprite table start, and thus see a terminator right away, and therefore have no sprites. 2680 | 2681 | ; Clear any status bit 2682 | in a, (VDP_STATUS) 2683 | ; Next we turn on the screen... without interrupts 2684 | SET_VDP_REGISTER 1, %11000000 2685 | ; And wait for a status update 2686 | ld b, 2 2687 | -:in a, (VDP_STATUS) 2688 | bit 7, a 2689 | and %11100000 ; mask off low bits (meaningless on SMS, collision index on TMS) 2690 | jr z, - 2691 | ; If we see collision but not overflow then we are in mode 4 2692 | cp %00100000 2693 | jr z, _IsMode4 2694 | ; If we see a frame interrupt then we try again for one frame 2695 | cp %10000000 2696 | jr nz, _NotMode4 2697 | djnz - 2698 | 2699 | _NotMode4: 2700 | xor a 2701 | jr + 2702 | _IsMode4: 2703 | ld a, 1 2704 | +:ld (IsSMSVDP), a 2705 | ; Turn the screen off again 2706 | SET_VDP_REGISTER 1, %10000000 2707 | ret 2708 | 2709 | ; Set up our VDP for display. Set the correct video mode, but the display is turned off. 2710 | SetUpVDP: 2711 | ; Set VDP registers (SMS) 2712 | ld hl, VDPRegisterInitialisation 2713 | ld b, _sizeof_VDPRegisterInitialisation 2714 | -:ld c, VDP_REGISTER 2715 | otir 2716 | ret 2717 | 2718 | SetUpVDP_TMS: 2719 | ; Set VDP registers (TMS mode) 2720 | ld hl, VDPRegisterInitialisation_TMS 2721 | ld b, _sizeof_VDPRegisterInitialisation_TMS 2722 | jp - 2723 | 2724 | ClearVRAM: 2725 | ; Clear VRAM (same for both) 2726 | SET_VRAM_ADDRESS 0 2727 | ld hl, 16*1024 ; 16KB VRAM 2728 | xor a 2729 | ld bc, 1 2730 | -:out (VDP_DATA), a 2731 | sbc hl, bc 2732 | jr nz, - 2733 | ret 2734 | 2735 | ; Install our palette into the VDP 2736 | LoadPalette: 2737 | SET_CRAM_ADDRESS 0 2738 | ld hl, Palette 2739 | ld b, _sizeof_Palette 2740 | ld c, VDP_DATA 2741 | otir 2742 | ret 2743 | 2744 | ; Load the font 2745 | LoadFont: 2746 | SET_VRAM_ADDRESS $20 * ' ' ; Load space at tile 32 so we can emit ASCII easily 2747 | ld hl, font_8x8 2748 | ld bc, _sizeof_font_8x8 2749 | -:ld a, (hl) 2750 | out (VDP_DATA), a 2751 | out (VDP_DATA), a 2752 | out (VDP_DATA), a 2753 | out (VDP_DATA), a 2754 | inc hl 2755 | dec bc 2756 | ld a, b 2757 | or c 2758 | jr nz, - 2759 | ret 2760 | 2761 | LoadFont_TMS: 2762 | SET_VRAM_ADDRESS 8 * ' ' 2763 | ld hl, font_6x8 2764 | ld bc, _sizeof_font_6x8 2765 | -:ld a, (hl) 2766 | out (VDP_DATA), a ; 1bpp 2767 | inc hl 2768 | dec bc 2769 | ld a, b 2770 | or c 2771 | jr nz, - 2772 | ret 2773 | 2774 | SetVRAMAddress: 2775 | ; address in hl, preserve a 2776 | push af 2777 | ld a, l 2778 | out (VDP_ADDRESS), a 2779 | ld a, h 2780 | out (VDP_ADDRESS), a 2781 | pop af 2782 | ret 2783 | 2784 | .ends 2785 | 2786 | .ifdef WriteToScreen 2787 | .section "Console emulation" free 2788 | 2789 | .define NAME_TABLE_START NameTableAddress | VRAM_WRITE_MASK 2790 | .define SCREEN_END NAME_TABLE_START + 32*23*2 2791 | .define NAME_TABLE_END NAME_TABLE_START + 32*28*2 2792 | 2793 | ; Initialize the cursor variables 2794 | InitCursor: 2795 | ld hl, NAME_TABLE_START 2796 | xor a 2797 | ld (CursorX), a 2798 | ld (VRAMAddress), hl 2799 | ld (Scroll), a 2800 | ld (ScrollFlag), a 2801 | ret 2802 | 2803 | ComputeTestCount: 2804 | ; Compute case count 2805 | ; The test will count through all bits in CounterBits 2806 | ; and for each it will shift a bit through ShifterBits. 2807 | ; This means we will run through 2808 | ; 2^popcount(CounterBits)*(popcount(ShifterBits)+1) 2809 | ; permutations for each test. 2810 | ; The indicator increments every 64 permutations, so we want calculate 2811 | ; 2^popcount(CounterBits)*(popcount(ShifterBits)+1)/64 2812 | ; The highest CounterBits is 16, so the result exceeds 16 bits before we divide by 64. 2813 | ; To avoid overflow, we try to divide by 64 early if possible. 2814 | ; The highest ShifterBits is 70. 2815 | ld de, Test.CounterBits 2816 | add hl, de 2817 | call _GetPopCount 2818 | add a, a 2819 | ld e, a ; save here, doubled. _GetPopCount does not touch e. 2820 | call _GetPopCount 2821 | inc a 2822 | ld b, a ; save here 2823 | 2824 | ; Compute 2^e 2825 | ; We want to divide by 64 later so if it's >6, we shift in advance 2826 | ; to keep things inside 16 bits. 2827 | ld a, e 2828 | sub 12 2829 | jr c, + 2830 | ld e, a 2831 | +: push af 2832 | ; Compute 2^(e/2) 2833 | ld hl, _powersOfTwo 2834 | ld d, 0 2835 | add hl, de 2836 | ld a, (hl) 2837 | inc hl 2838 | ld h, (hl) 2839 | ld l, a 2840 | ; Now multiply by b. We assume it is small so we just repeatedly add. 2841 | ex de, hl 2842 | ld hl, 0 2843 | -: add hl, de 2844 | djnz - 2845 | pop af 2846 | ; Now check if we need to divide by 64 at the end 2847 | ret nc 2848 | ; We want to round up, do 64 -> 1, 65 -> 2 2849 | ; Check if there's anything in the low 6 bits 2850 | ld a, l 2851 | .repeat 6 2852 | srl h 2853 | rr l 2854 | .endr 2855 | ; If the low 6 bits of a have anything in, increment 2856 | and 63 2857 | jr z, + 2858 | inc hl 2859 | +: ; We might have run out of bits, but we don't want to show 0 2860 | ld a, h 2861 | or l 2862 | ret nz 2863 | inc hl 2864 | ret 2865 | 2866 | _powersOfTwo: 2867 | .dw 1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048, 4096, 8192, 16385, 32768 2868 | 2869 | _GetPopCount: 2870 | ld a, 0 ; accumulate to here 2871 | ld c, _sizeof_TestCase 2872 | --: 2873 | ld d, (hl) ; data in here 2874 | inc hl 2875 | ld b, 8 2876 | -:srl d ; Shift bit out of d 2877 | adc a, 0 ; Add to a if set 2878 | djnz - 2879 | dec c 2880 | jr nz, -- 2881 | ret 2882 | 2883 | ; Code to print slash between executions 2884 | ; Only outputs to screen 2885 | UpdateProgressIndicator: 2886 | ; Increment the counter 2887 | ld a, (ProgressCounter) 2888 | inc a 2889 | ld (ProgressCounter), a 2890 | 2891 | and %00111111 ; Update every 64 cases 2892 | ret nz 2893 | 2894 | ld hl, (CaseCount) 2895 | inc hl 2896 | ld (CaseCount), hl 2897 | 2898 | ld a, (ProgressCounter) 2899 | rla 2900 | rla 2901 | rla 2902 | and %00000011 2903 | 2904 | push af 2905 | push bc 2906 | push hl 2907 | ld hl, (VRAMAddressForCounter) 2908 | call SetVRAMAddress 2909 | 2910 | ; Print the case count 2911 | ld b, 0 ; High byte for SMS mode 2912 | ld c, VDP_DATA 2913 | ld hl, (CaseCount) 2914 | ld a, h 2915 | .repeat 4 2916 | rrca 2917 | .endr 2918 | call _nibble 2919 | ld a, h 2920 | call _nibble 2921 | ld a, l 2922 | .repeat 4 2923 | rrca 2924 | .endr 2925 | call _nibble 2926 | ld a, l 2927 | call _nibble 2928 | pop hl 2929 | pop bc 2930 | pop af 2931 | ret 2932 | 2933 | _nibble: 2934 | and $0f 2935 | cp 10 2936 | jp c, + 2937 | add a, 'a'-'9'-1 2938 | +:add a, '0' 2939 | out (c), a 2940 | ld a, (IsSMSVDP) 2941 | or a 2942 | ret z 2943 | add a, 0 2944 | add a, 0 2945 | out (c), b 2946 | ret 2947 | 2948 | ; Code to print a character on the screen. Does stuff like handle 2949 | ; line feeds, scrolling, etc. 2950 | 2951 | PrintChar_SMS: 2952 | push af 2953 | push de 2954 | ; If it's a carriage return, skip it. 2955 | cp NEWLINE 2956 | jp z, _NewLine 2957 | ; Write the character (in a) to the screen 2958 | ld b, a ; save value for later 2959 | ld c, VDP_ADDRESS 2960 | ld hl, (VRAMAddress) 2961 | call SetVRAMAddress 2962 | 2963 | ld c, VDP_DATA 2964 | out (c), a ; Output the character 2965 | 2966 | ld a, (IsSMSVDP) 2967 | dec a 2968 | jr nz, + 2969 | ; SMS mode: write upper byte (we just made a zero) 2970 | out (c), a 2971 | inc hl ; and move pointer on an extra byte 2972 | +: inc hl 2973 | ld (VRAMAddress), hl 2974 | 2975 | ; Move cursor forward 2976 | ld hl, CursorX 2977 | inc (hl) 2978 | 2979 | ; Check if we're at the end of the line 2980 | ld a, (IsSMSVDP) 2981 | or a 2982 | jr z, + 2983 | 2984 | ld a, (CursorX) 2985 | cp 31 2986 | jp nz, _PrintCharDone 2987 | jr ++ 2988 | 2989 | +: ld a, (CursorX) 2990 | cp 40 2991 | jp nz, _PrintCharDone 2992 | 2993 | ++: ; We insert a newline, to make sure scrolling etc works as intended. 2994 | ; We then want to suppress any subsequent newline. 2995 | ld a, 1 2996 | ld (NewlineAdded), a 2997 | jp _NextLine 2998 | 2999 | _PrintCharDone: 3000 | xor a 3001 | ld (NewlineAdded), a 3002 | pop de 3003 | pop af 3004 | ret 3005 | 3006 | _NewLine: 3007 | ; If NewlineAdded is set, we reset it and do nothing 3008 | ld a, (NewlineAdded) 3009 | or a 3010 | jr z, _NextLine 3011 | jr _PrintCharDone 3012 | 3013 | ; Here we do the job of scrolling the display, computing the 3014 | ; new VRAM address, and all that fun stuff. 3015 | _NextLine: 3016 | ld hl, (VRAMAddress) ; Increase the VRAM position 3017 | ; Get the cursor position and find out how far it was to the end of the line. 3018 | ld a, (CursorX) 3019 | ld b, a 3020 | 3021 | ; Here we fork for TMS vs. SMS 3022 | ld a, (IsSMSVDP) 3023 | or a 3024 | jr z, _TMSNextLine 3025 | 3026 | ; SMS 3027 | ld a, 32 3028 | sub b 3029 | add a, a ; two bytes per tile 3030 | call _WriteBlanks ; Fill the rest of the line 3031 | ld c, a ; Now, add this to HL. This is the new address. 3032 | ld b, 0 3033 | add hl, bc ; Now create new address. 3034 | ccf 3035 | push hl 3036 | ld bc, SCREEN_END ; Check if we got to the bottom of the screen 3037 | sbc hl, bc 3038 | jp c, + 3039 | ld a, 1 ; If we are, set the scroll flag on... we scroll from now on. 3040 | ld (ScrollFlag), a 3041 | +: pop hl 3042 | push hl 3043 | ld bc, NAME_TABLE_END ; Next, check if we're at the end of VRAM 3044 | sbc hl, bc 3045 | pop hl 3046 | jp c, + 3047 | ld hl, NAME_TABLE_START ; If we are, return to the top of VRAM. 3048 | +: ld (VRAMAddress), hl ; Now, save our VRAM address. 3049 | ld a, 32*2 ; Clear the new line 3050 | call _WriteBlanks 3051 | ld a, (ScrollFlag) ; Load the Scroll flag and check if it's set. 3052 | or a 3053 | jp z, ++ 3054 | ld a, (Scroll) ; If it is, increase the Scroll value, and wrap at the bottom of the screen. 3055 | inc a 3056 | cp 28 3057 | jp nz, + 3058 | xor a 3059 | +: ld (Scroll), a ; Now, write out Scroll value out to the VDP. 3060 | sla a 3061 | sla a 3062 | sla a 3063 | ld c, $bf 3064 | out (c), a 3065 | ld a, $89 3066 | out (c), a 3067 | ++: xor a ; Reset the cursor X position to 0 3068 | ld (CursorX), a 3069 | pop de 3070 | pop af 3071 | ret 3072 | 3073 | _TMSNextLine: 3074 | ld a, 40 3075 | sub b 3076 | call _WriteBlanks ; Fill the rest of the line 3077 | ld c, a ; Now add this to HL. This is the new address. 3078 | ld b, 0 3079 | add hl, bc ; Now create new address. 3080 | push hl 3081 | ld bc, NAME_TABLE_START + 24 * 40 ; Check if we got to the bottom of the screen/name table 3082 | sbc hl, bc 3083 | pop hl 3084 | jp nz, + 3085 | ld a, 1 ; If we are, set the scroll flag on... we scroll from now on. 3086 | ld (ScrollFlag), a 3087 | ld hl, NAME_TABLE_START + 23 * 40 ; If we are, return to the start of the last line 3088 | +: ld (VRAMAddress), hl ; Now, save our VRAM address. 3089 | ld a, (ScrollFlag) ; Load the Scroll flag and check if it's set. 3090 | or a 3091 | jp z, ++ 3092 | 3093 | ; We scroll by copying all the name table data up the screen... 3094 | ; We read in 40 bytes at a time and write back one row higher 3095 | ld b, 23 ; rows to copy 3096 | ld hl, NameTableAddress + 40 ; write bit unset 3097 | --: push bc 3098 | call SetVRAMAddress 3099 | 3100 | push hl 3101 | ld b, 40 ; columns 3102 | ld hl, TMSCopyBuffer 3103 | ld c, VDP_DATA 3104 | -: ini ; 16 3105 | jr nz, - ; 12 -> 28 cycles total (inir is 21 which is too fast) 3106 | pop hl 3107 | 3108 | ; then write one row higher 3109 | ld bc, VRAM_WRITE_MASK - 40 ; write bit, -40 for one row higher 3110 | add hl, bc 3111 | call SetVRAMAddress 3112 | push hl 3113 | ld b, 40 ; columns 3114 | ld hl, TMSCopyBuffer 3115 | ld c, VDP_DATA 3116 | -: outi 3117 | jr nz, - ; same timing as ini loop above 3118 | pop hl 3119 | ; Now set the new read address. 3120 | ; HL is pointing at the start of the row we just copied into. 3121 | ; We add two rows plus we clear the write flag 3122 | ld bc, -VRAM_WRITE_MASK + 40 * 2 3123 | add hl, bc 3124 | pop bc 3125 | djnz -- 3126 | 3127 | ; Clear the new line 3128 | ld hl, (VRAMAddress) 3129 | ld a, 40 3130 | call _WriteBlanks 3131 | 3132 | 3133 | ++ xor a ; Reset the cursor X position to 0 3134 | ld (CursorX), a 3135 | pop de 3136 | pop af 3137 | ret 3138 | 3139 | 3140 | ; Fill the nametable from HL with A bytes. 3141 | _WriteBlanks: 3142 | push af 3143 | push bc 3144 | ; Do nothing for count = 0 3145 | or a 3146 | jr z, + 3147 | ld b, a ; save count 3148 | call SetVRAMAddress 3149 | 3150 | ; Now, zero out the region 3151 | +: xor a 3152 | -:out (VDP_DATA), a ; 11 Output the character 3153 | dec b ; 4 3154 | jr nz, - ; 12 -> 27 cycles between writes (djnz would give 24) 3155 | +:pop bc 3156 | pop af 3157 | ret 3158 | .ends 3159 | .endif 3160 | 3161 | .ifdef WriteToSRAM 3162 | .ramsection "SRAM writing variables" slot 3 3163 | SRAMPointer dw ; Next address to write to 3164 | .ends 3165 | .endif 3166 | 3167 | .section "SRAM support" free 3168 | .define SRAM_CONTROL $fffc 3169 | .define SRAM_ENABLED $08 3170 | .define SRAM_START $8000 3171 | .define SRAM_SIZE $4000 ; 16KB - if real SRAM is smaller, we will just double-blank it on startup. 3172 | 3173 | InitialiseSRAM: 3174 | ; Clue in any mapper detection 3175 | xor a 3176 | ld ($ffff), a 3177 | 3178 | ; Page in SRAM 3179 | ld a, SRAM_ENABLED 3180 | ld (SRAM_CONTROL), a 3181 | 3182 | ; Results address 3183 | ld hl, SRAM_START 3184 | ld (SRAMPointer), hl 3185 | 3186 | ; Clear SRAM to whitespace 3187 | ld bc, SRAM_SIZE 3188 | -:ld a, ' ' 3189 | ld (hl), a 3190 | inc hl 3191 | dec bc 3192 | ld a, b 3193 | or c 3194 | jr nz, - 3195 | ret 3196 | 3197 | PrintChar_SRAM: 3198 | push hl 3199 | ; Write to SRAM 3200 | ld hl, (SRAMPointer) 3201 | ld (hl), a 3202 | inc hl 3203 | ld (SRAMPointer), hl 3204 | pop hl 3205 | ret 3206 | 3207 | .ends 3208 | 3209 | ; SDSC tag and SMS rom header 3210 | .sdsctag \ 3211 | 0.21, \ 3212 | "Z80 Instruction Exerciser", \ 3213 | "Based on ZEXALL by Frank Cringle, " \ 3214 | "with credit to J.G.Harston\n" \ 3215 | "See https://www.smspower.org/Homebrew/ZEXALL-SMS\n" \ 3216 | "Fonts by Damien Guard, see https://damieng.com/typography/zx-origins/", \ 3217 | "FluBBa, Maxim, Eric R. Quinn, Brett K, asynchronous, and others on the SMS Power! forums" 3218 | --------------------------------------------------------------------------------