From b87826ae62a28aa27323149c72cfb45e7e5ce1eb Mon Sep 17 00:00:00 2001 From: bwmott Date: Thu, 27 Dec 2001 19:54:36 +0000 Subject: [PATCH] This commit was generated by cvs2svn to compensate for changes in r6, which included commits to RCS files with non-trunk default branches. git-svn-id: svn://svn.code.sf.net/p/stella/code/trunk@7 8b62c5a3-ac7e-4cc8-8f21-d9a121418aba --- stella/Announce.txt | 82 + stella/Changes.txt | 177 + stella/Copyright.txt | 37 + stella/License.txt | 339 ++ stella/Readme.txt | 13 + stella/Todo.txt | 39 + stella/src/build/DefProps.def | 5246 +++++++++++++++++ stella/src/build/M6502Hi.ins | 3194 ++++++++++ stella/src/build/M6502Low.ins | 3188 ++++++++++ stella/src/build/makefile | 319 + stella/src/emucore/Booster.cxx | 105 + stella/src/emucore/Booster.hxx | 78 + stella/src/emucore/Cart.cxx | 215 + stella/src/emucore/Cart.hxx | 80 + stella/src/emucore/Cart2K.cxx | 82 + stella/src/emucore/Cart2K.hxx | 93 + stella/src/emucore/Cart3F.cxx | 148 + stella/src/emucore/Cart3F.hxx | 112 + stella/src/emucore/Cart4K.cxx | 82 + stella/src/emucore/Cart4K.hxx | 92 + stella/src/emucore/CartAR.cxx | 420 ++ stella/src/emucore/CartAR.hxx | 150 + stella/src/emucore/CartE0.cxx | 191 + stella/src/emucore/CartE0.hxx | 121 + stella/src/emucore/CartE7.cxx | 214 + stella/src/emucore/CartE7.hxx | 137 + stella/src/emucore/CartF4SC.cxx | 154 + stella/src/emucore/CartF4SC.hxx | 105 + stella/src/emucore/CartF6.cxx | 165 + stella/src/emucore/CartF6.hxx | 102 + stella/src/emucore/CartF6SC.cxx | 198 + stella/src/emucore/CartF6SC.hxx | 105 + stella/src/emucore/CartF8.cxx | 145 + stella/src/emucore/CartF8.hxx | 102 + stella/src/emucore/CartF8SC.cxx | 178 + stella/src/emucore/CartF8SC.hxx | 105 + stella/src/emucore/CartFASC.cxx | 188 + stella/src/emucore/CartFASC.hxx | 105 + stella/src/emucore/CartFE.cxx | 81 + stella/src/emucore/CartFE.hxx | 103 + stella/src/emucore/CartMC.cxx | 218 + stella/src/emucore/CartMC.hxx | 207 + stella/src/emucore/Console.cxx | 261 + stella/src/emucore/Console.hxx | 159 + stella/src/emucore/Control.cxx | 54 + stella/src/emucore/Control.hxx | 152 + stella/src/emucore/DefProps.cxx | 35 + stella/src/emucore/DefProps.hxx | 31 + stella/src/emucore/Driving.cxx | 128 + stella/src/emucore/Driving.hxx | 84 + stella/src/emucore/Event.cxx | 48 + stella/src/emucore/Event.hxx | 102 + stella/src/emucore/Joystick.cxx | 76 + stella/src/emucore/Joystick.hxx | 76 + stella/src/emucore/Keyboard.cxx | 207 + stella/src/emucore/Keyboard.hxx | 80 + stella/src/emucore/M6532.cxx | 306 + stella/src/emucore/M6532.hxx | 129 + stella/src/emucore/MD5.cxx | 347 ++ stella/src/emucore/MD5.hxx | 35 + stella/src/emucore/MediaSrc.cxx | 44 + stella/src/emucore/MediaSrc.hxx | 105 + stella/src/emucore/Paddles.cxx | 76 + stella/src/emucore/Paddles.hxx | 76 + stella/src/emucore/Props.cxx | 258 + stella/src/emucore/Props.hxx | 150 + stella/src/emucore/PropsSet.cxx | 206 + stella/src/emucore/PropsSet.hxx | 128 + stella/src/emucore/Random.cxx | 53 + stella/src/emucore/Random.hxx | 69 + stella/src/emucore/Sound.cxx | 44 + stella/src/emucore/Sound.hxx | 71 + stella/src/emucore/Switches.cxx | 111 + stella/src/emucore/Switches.hxx | 66 + stella/src/emucore/TIA.cxx | 2698 +++++++++ stella/src/emucore/TIA.hxx | 422 ++ stella/src/emucore/m6502/Copyright.txt | 37 + stella/src/emucore/m6502/License.txt | 339 ++ stella/src/emucore/m6502/src/D6502.cxx | 187 + stella/src/emucore/m6502/src/D6502.hxx | 152 + stella/src/emucore/m6502/src/Device.cxx | 37 + stella/src/emucore/m6502/src/Device.hxx | 95 + stella/src/emucore/m6502/src/M6502.cxx | 335 ++ stella/src/emucore/m6502/src/M6502.hxx | 219 + stella/src/emucore/m6502/src/M6502.m4 | 1410 +++++ stella/src/emucore/m6502/src/M6502Hi.cxx | 169 + stella/src/emucore/m6502/src/M6502Hi.hxx | 108 + stella/src/emucore/m6502/src/M6502Hi.m4 | 314 + stella/src/emucore/m6502/src/M6502Low.cxx | 159 + stella/src/emucore/m6502/src/M6502Low.hxx | 94 + stella/src/emucore/m6502/src/M6502Low.m4 | 286 + stella/src/emucore/m6502/src/NullDev.cxx | 60 + stella/src/emucore/m6502/src/NullDev.hxx | 86 + stella/src/emucore/m6502/src/System.cxx | 160 + stella/src/emucore/m6502/src/System.hxx | 310 + .../src/emucore/m6502/src/bspf/Copyright.txt | 37 + stella/src/emucore/m6502/src/bspf/License.txt | 339 ++ .../src/emucore/m6502/src/bspf/src/bspf.hxx | 72 + stella/src/emucore/misc/romtohex.cxx | 29 + stella/src/emucore/misc/scrom.asm | 81 + stella/src/emucore/stella.pro | 5246 +++++++++++++++++ stella/src/ui/dos/PCJoys.cxx | 208 + stella/src/ui/dos/PCJoys.hxx | 71 + stella/src/ui/dos/SndDOS.cxx | 129 + stella/src/ui/dos/SndDOS.hxx | 66 + stella/src/ui/dos/mainDOS.cxx | 792 +++ stella/src/ui/dos/sbdrv.c | 988 ++++ stella/src/ui/dos/sbdrv.h | 90 + stella/src/ui/dos/sbdrv.txt | 218 + stella/src/ui/dos/scandef.h | 462 ++ stella/src/ui/sound/OSS.c | 227 + stella/src/ui/sound/SndUnix.cxx | 234 + stella/src/ui/sound/SndUnix.hxx | 87 + stella/src/ui/sound/TIASound.c | 612 ++ stella/src/ui/sound/TIASound.h | 54 + stella/src/ui/sound/TIASound.txt | 297 + stella/src/ui/sound/makefile | 36 + stella/src/ui/x11/mainX11.cxx | 912 +++ 118 files changed, 39946 insertions(+) create mode 100644 stella/Announce.txt create mode 100644 stella/Changes.txt create mode 100644 stella/Copyright.txt create mode 100644 stella/License.txt create mode 100644 stella/Readme.txt create mode 100644 stella/Todo.txt create mode 100644 stella/src/build/DefProps.def create mode 100644 stella/src/build/M6502Hi.ins create mode 100644 stella/src/build/M6502Low.ins create mode 100644 stella/src/build/makefile create mode 100644 stella/src/emucore/Booster.cxx create mode 100644 stella/src/emucore/Booster.hxx create mode 100644 stella/src/emucore/Cart.cxx create mode 100644 stella/src/emucore/Cart.hxx create mode 100644 stella/src/emucore/Cart2K.cxx create mode 100644 stella/src/emucore/Cart2K.hxx create mode 100644 stella/src/emucore/Cart3F.cxx create mode 100644 stella/src/emucore/Cart3F.hxx create mode 100644 stella/src/emucore/Cart4K.cxx create mode 100644 stella/src/emucore/Cart4K.hxx create mode 100644 stella/src/emucore/CartAR.cxx create mode 100644 stella/src/emucore/CartAR.hxx create mode 100644 stella/src/emucore/CartE0.cxx create mode 100644 stella/src/emucore/CartE0.hxx create mode 100644 stella/src/emucore/CartE7.cxx create mode 100644 stella/src/emucore/CartE7.hxx create mode 100644 stella/src/emucore/CartF4SC.cxx create mode 100644 stella/src/emucore/CartF4SC.hxx create mode 100644 stella/src/emucore/CartF6.cxx create mode 100644 stella/src/emucore/CartF6.hxx create mode 100644 stella/src/emucore/CartF6SC.cxx create mode 100644 stella/src/emucore/CartF6SC.hxx create mode 100644 stella/src/emucore/CartF8.cxx create mode 100644 stella/src/emucore/CartF8.hxx create mode 100644 stella/src/emucore/CartF8SC.cxx create mode 100644 stella/src/emucore/CartF8SC.hxx create mode 100644 stella/src/emucore/CartFASC.cxx create mode 100644 stella/src/emucore/CartFASC.hxx create mode 100644 stella/src/emucore/CartFE.cxx create mode 100644 stella/src/emucore/CartFE.hxx create mode 100644 stella/src/emucore/CartMC.cxx create mode 100644 stella/src/emucore/CartMC.hxx create mode 100644 stella/src/emucore/Console.cxx create mode 100644 stella/src/emucore/Console.hxx create mode 100644 stella/src/emucore/Control.cxx create mode 100644 stella/src/emucore/Control.hxx create mode 100644 stella/src/emucore/DefProps.cxx create mode 100644 stella/src/emucore/DefProps.hxx create mode 100644 stella/src/emucore/Driving.cxx create mode 100644 stella/src/emucore/Driving.hxx create mode 100644 stella/src/emucore/Event.cxx create mode 100644 stella/src/emucore/Event.hxx create mode 100644 stella/src/emucore/Joystick.cxx create mode 100644 stella/src/emucore/Joystick.hxx create mode 100644 stella/src/emucore/Keyboard.cxx create mode 100644 stella/src/emucore/Keyboard.hxx create mode 100644 stella/src/emucore/M6532.cxx create mode 100644 stella/src/emucore/M6532.hxx create mode 100644 stella/src/emucore/MD5.cxx create mode 100644 stella/src/emucore/MD5.hxx create mode 100644 stella/src/emucore/MediaSrc.cxx create mode 100644 stella/src/emucore/MediaSrc.hxx create mode 100644 stella/src/emucore/Paddles.cxx create mode 100644 stella/src/emucore/Paddles.hxx create mode 100644 stella/src/emucore/Props.cxx create mode 100644 stella/src/emucore/Props.hxx create mode 100644 stella/src/emucore/PropsSet.cxx create mode 100644 stella/src/emucore/PropsSet.hxx create mode 100644 stella/src/emucore/Random.cxx create mode 100644 stella/src/emucore/Random.hxx create mode 100644 stella/src/emucore/Sound.cxx create mode 100644 stella/src/emucore/Sound.hxx create mode 100644 stella/src/emucore/Switches.cxx create mode 100644 stella/src/emucore/Switches.hxx create mode 100644 stella/src/emucore/TIA.cxx create mode 100644 stella/src/emucore/TIA.hxx create mode 100644 stella/src/emucore/m6502/Copyright.txt create mode 100644 stella/src/emucore/m6502/License.txt create mode 100644 stella/src/emucore/m6502/src/D6502.cxx create mode 100644 stella/src/emucore/m6502/src/D6502.hxx create mode 100644 stella/src/emucore/m6502/src/Device.cxx create mode 100644 stella/src/emucore/m6502/src/Device.hxx create mode 100644 stella/src/emucore/m6502/src/M6502.cxx create mode 100644 stella/src/emucore/m6502/src/M6502.hxx create mode 100644 stella/src/emucore/m6502/src/M6502.m4 create mode 100644 stella/src/emucore/m6502/src/M6502Hi.cxx create mode 100644 stella/src/emucore/m6502/src/M6502Hi.hxx create mode 100644 stella/src/emucore/m6502/src/M6502Hi.m4 create mode 100644 stella/src/emucore/m6502/src/M6502Low.cxx create mode 100644 stella/src/emucore/m6502/src/M6502Low.hxx create mode 100644 stella/src/emucore/m6502/src/M6502Low.m4 create mode 100644 stella/src/emucore/m6502/src/NullDev.cxx create mode 100644 stella/src/emucore/m6502/src/NullDev.hxx create mode 100644 stella/src/emucore/m6502/src/System.cxx create mode 100644 stella/src/emucore/m6502/src/System.hxx create mode 100644 stella/src/emucore/m6502/src/bspf/Copyright.txt create mode 100644 stella/src/emucore/m6502/src/bspf/License.txt create mode 100644 stella/src/emucore/m6502/src/bspf/src/bspf.hxx create mode 100644 stella/src/emucore/misc/romtohex.cxx create mode 100644 stella/src/emucore/misc/scrom.asm create mode 100644 stella/src/emucore/stella.pro create mode 100644 stella/src/ui/dos/PCJoys.cxx create mode 100644 stella/src/ui/dos/PCJoys.hxx create mode 100644 stella/src/ui/dos/SndDOS.cxx create mode 100644 stella/src/ui/dos/SndDOS.hxx create mode 100644 stella/src/ui/dos/mainDOS.cxx create mode 100644 stella/src/ui/dos/sbdrv.c create mode 100644 stella/src/ui/dos/sbdrv.h create mode 100644 stella/src/ui/dos/sbdrv.txt create mode 100644 stella/src/ui/dos/scandef.h create mode 100644 stella/src/ui/sound/OSS.c create mode 100644 stella/src/ui/sound/SndUnix.cxx create mode 100644 stella/src/ui/sound/SndUnix.hxx create mode 100644 stella/src/ui/sound/TIASound.c create mode 100644 stella/src/ui/sound/TIASound.h create mode 100644 stella/src/ui/sound/TIASound.txt create mode 100644 stella/src/ui/sound/makefile create mode 100644 stella/src/ui/x11/mainX11.cxx diff --git a/stella/Announce.txt b/stella/Announce.txt new file mode 100644 index 000000000..81d9fbe04 --- /dev/null +++ b/stella/Announce.txt @@ -0,0 +1,82 @@ +=============================================================================== + + SSSS tt lll lll + SS SS tt ll ll + SS tttttt eeee ll ll aaaa + SSSS tt ee ee ll ll aa + SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" + SS SS tt ee ll ll aa aa + SSSS ttt eeeee llll llll aaaaa + +=============================================================================== + Release 1.1 for DOS, Linux, and Unix +=============================================================================== + +The Atari 2600 Video Computer System (VCS), introduced in 1977, was the most +popular home video game system of the early 1980's. Now you can enjoy all of +your favorite Atari 2600 games on your PC thanks to Stella! + +Stella is a multi-platform Atari 2600 VCS emulator written in C++. Stella +allows you to play most of the games written for the Atari 2600 including +Supercharger games. + +This is the 1.1 release of Stella for DOS, Linux, and Unix. Distributions +for other operating systems will appear as they become available. The three +distributions currently available are: + + * Binary distribution for Linux (stella-1.1-linux-x86.tar.gz) + + * Binary distribution for DOS (st11.zip) + + * Source code distribution for Unix and DOS (stella-1.1-src.tar.gz) + +A few 2600 ROM images, BIN files, are included with these distributions. +BEYOND THESE FILES NONE OF THE DISTRIBUTIONS CONTAIN ANY 2600 ROM IMAGES. +PLEASE DON'T WRITE ASKING FOR ROMS BECAUSE THEY ARE COPYRIGHTED! If you +own any of the Atari 2600 Action Packs by Activision you can use the ROM +images from them with Stella. + +New in this Release +=================== + + * DOS and Linux versions support real Atari 2600 paddles using a + special PC game port adaptor + + * Linux version uses the new 1.2.x joystick driver API + + * Added support for the "-display" option to the X Window version + + * Added support for private colormaps to the X Window version + + * Fixed a few bugs in the Supercharger emulation + + - A major bug in the ROM loading routine was fixed + + - Multi-loading in "Escape from the Mindmaster" works correctly + + - All Supercharger games load and execute at this point + + * Added a small hack to the TIA code to fix a display problem in "Escape + from the Mindmaster" + + * Improved TIA emulation to support the RESPx multi-sprite trick + +Distribution Site +================= + +The Stella distributions can be obtained from the Stella home page at: + + http://stella.atari.org + +If for some reason you are unable to connect to this page please try +again later or try http://www4.ncsu.edu/~bwmott/2600/. + +Contacts +======== + +If you have any questions regarding Stella send mail to: + + Bradford W. Mott (bwmott@acm.org) + +Have Fun! + diff --git a/stella/Changes.txt b/stella/Changes.txt new file mode 100644 index 000000000..bc160dbdb --- /dev/null +++ b/stella/Changes.txt @@ -0,0 +1,177 @@ +=============================================================================== + + SSSS tt lll lll + SS SS tt ll ll + SS tttttt eeee ll ll aaaa + SSSS tt ee ee ll ll aa + SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" + SS SS tt ee ll ll aa aa + SSSS ttt eeeee llll llll aaaaa + +=============================================================================== + Release History +=============================================================================== + +1.0 to 1.1: (February 26, 1999) + + * DOS and Linux versions support real Atari 2600 paddles using a + special PC game port adaptor + + * Linux version uses the new 1.2.x joystick driver API + + * Added support for the "-display" option to the X Window version + + * Added support for private colormaps to the X Window version + + * Fixed a few bugs in the Supercharger emulation + + - A major bug in the ROM loading routine was fixed + + - Multi-loading in "Escape from the Mindmaster" works correctly + + - All Supercharger games load and execute at this point + + * Added a small hack to the TIA code to fix a display problem in "Escape + from the Mindmaster" + + * Improved TIA emulation to support the RESPx multi-sprite trick + +1.0b1 to 1.0: (October 7, 1998) + + * DOS version supports 320x200 and 320x240 graphics modes + + * Several portability issues have been resolved + + * Preliminary support for Chris Wilkson's Megacart bank-switching scheme + + * BSDI target included in makefile + + * Improved Users Manual in several "popular" formats + +0.7 to 1.0b1: (July 25, 1998) + + * Supports the following controllers: + + Joysticks, Paddles, Booster-Grip, Keyboard and Driving + + * Supports the following bank switching methods: + + 2K, 3F, 4K, AR, E0, E7, F4SC, F6, F8, F8SC, FASC, FE + + * Properties are associated with games using their MD5 checksum + calculated on the entire ROM image + + * Uses the new 'stella.pro' file format for game properties + + * Includes Erik's latest stella.pro properties file + + * New frame rate throttle code for X windows GUI + + * Based on the new and improved M6502 CPU emulation + + * Improvements to TIA emulation + + - Support HMOVE blanks + + - Improved Cosmic Ark star field effect + + - Some support for the RESPx multiple sprite trick + + - Support NTSC and PAL palettes + + * Improvements to PIA emulation (timing) + + * Improved Supercharger emulation + +0.6 to 0.7: (June 7, 1997) + + * Improved emulation speed of TIA and 6507 + + * Added Starpath Supercharger support + + * Added Tigervision bank-switching support (3F bank-switching) + + * Added pause game feature for Unix and DOS + + * VCS files combined into a single builtin property file + + * Added TIA HMOVE "feature" to support Cosmic Ark stars + + * Improved TIA VSYNC code so that it works more like the real + thing (0.6 VSYNC code caused the graphics of some games to + be off such as Alien and Battle Zone) + + * Added two 6507 emulators: one is designed to act more like + the real thing, while the other is designed to be as fast as + possible (required for Supercharger support) + + * Changed TIA peeking so lower nibble of byte read is the same + as the TIA address being accessed (Warlords now works) + +0.5 to 0.6: (January 18, 1997) + + * Fixed collision detection problem (Freeway works) + + * Changed PIA timing code to fix screen jitters + + * Added new bank-switching methods: F4SC (Fatal Run), E7 (Burgertime) + + * Fixed some code in the TIA emulation that caused SEGFAULTS + + * Improved frame rate throttling code to work better on fast machines + + * Improved TIA emulation (missle graphics are fully emulated now) + + * Included Bob Colbert's "Okie Dokie" game + + * Uses version 1.1 of the TIA Sound library by Ron Fries + +0.4 to 0.5: (November 17, 1996) + + * Added sound support + + * Added new bank-switching methods: F8SC (Defender II), FASC (CBS RAM+) + + * Changed TIA so peeking $E and $F return $F not $0 for Haunted House + + * Changed PIA timing code to fix screen jitters in Frogger + + * Addressing scheme rewritten + + * Optimized 6507 memory accesses + + * Randomized memory in PIA upon startup + + * Removed auto-disabling of objects at the start of a frame + so you can't walk through walls in Adventure + + * Changed the X windows terminal update method to make it faster and + easier to understand + +0.3 to 0.4 (August 28, 1996): + + * TIA code has been optimized some + + * Some games can be played with just a ROM image + + * New search method for ROM images (no more STELLA_PATH) + + * Delta screen update supported + + * Better error handling added to the "core" + +0.2 to 0.3 (July 12, 1996): + + * Keyboard joystick support is much better (Daniel Marks) + + * Paddles are now supported via the mouse (Bradford Mott) + + * Many portability issues have been resolved (Keith Wilkins) + + * Fixed a problem with the 6507 ADC and SBC instructions that caused + some games (Enduro) not to work correctly (Bradford Mott) + + * Power Macintosh port (Aaron Giles) + + * Windows 95 & NT port (Jeff Miller) + diff --git a/stella/Copyright.txt b/stella/Copyright.txt new file mode 100644 index 000000000..334684b26 --- /dev/null +++ b/stella/Copyright.txt @@ -0,0 +1,37 @@ +=============================================================================== + + SSSS tt lll lll + SS SS tt ll ll + SS tttttt eeee ll ll aaaa + SSSS tt ee ee ll ll aa + SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" + SS SS tt ee ll ll aa aa + SSSS ttt eeeee llll llll aaaaa + +=============================================================================== + License Information and Copyright Notice +=============================================================================== + +Copyright (C) 1995-2002 Bradford W. Mott + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or any later version. + +You should have received a copy of the GNU General Public License version 2 +along with this program (License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY +PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES +THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN +"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE +MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + diff --git a/stella/License.txt b/stella/License.txt new file mode 100644 index 000000000..4189933be --- /dev/null +++ b/stella/License.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/stella/Readme.txt b/stella/Readme.txt new file mode 100644 index 000000000..aced02805 --- /dev/null +++ b/stella/Readme.txt @@ -0,0 +1,13 @@ + +This is release 1.1 of Stella. You'll find the Stella Users Manual in +the docs subdirectory. If you'd like to verify that you have the latest +release of Stella visit the web site at: + + http://stella.atari.org + +If you find any problems please let me know. + +Bradford W. Mott +February 26, 1999 +bwmott@acm.org + diff --git a/stella/Todo.txt b/stella/Todo.txt new file mode 100644 index 000000000..8bb47f3cc --- /dev/null +++ b/stella/Todo.txt @@ -0,0 +1,39 @@ +=============================================================================== + + SSSS tt lll lll + SS SS tt ll ll + SS tttttt eeee ll ll aaaa + SSSS tt ee ee ll ll aa + SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" + SS SS tt ee ll ll aa aa + SSSS ttt eeeee llll llll aaaaa + +=============================================================================== + To Do List - October 2, 1998 +=============================================================================== + +If you would like to contribute to Stella's development then find something +on the list below and send email to Bradford Mott at bwmott@acm.org. + + * Provide suggestions for improving Stella + + * Provide suggestions for improving the Stella Users Manual + + * Port Stella to other operating systems: + + - Atari ST/TT/Falcon + - Mac (680x0) + - Others... + + * Port the OSS stella-sound program, for Linux, to other versions of Unix + + * Add a built-in game menu to the DOS version of Stella + + * Find out if the Pro Audio Spectrum sound card is supported by the + DOS version of Stella (if not then add support for this card) + + * Add support for real Atari controllers to the DOS version of Stella + (this will involve hardware as well as software) + + * Add a debugger to Stella for game developers + diff --git a/stella/src/build/DefProps.def b/stella/src/build/DefProps.def new file mode 100644 index 000000000..52397313c --- /dev/null +++ b/stella/src/build/DefProps.def @@ -0,0 +1,5246 @@ +";", +"; LAST MODIFIED: 22 March 1999 1:32PM", +";", +"", +"\"Cartridge.MD5\" \"0db4f4150fecf77e4ce72ca4d04c052f\"", +"\"Cartridge.Name\" \"3D Tic-Tac-Toe\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2618 / 4975123\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"37\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"291bcdb05f2b37cdf9452d2bf08e0321\"", +"\"Cartridge.Name\" \"32 in 1\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26163\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c00734a2233ef683d9b6e622ac97a5c8\"", +"\"Cartridge.Name\" \"A-Team, The\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26133\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"17ee23e5da931be82f733917adcb6386\"", +"\"Cartridge.Name\" \"Acid Drop\"", +"\"Cartridge.Manufacturer\" \"Salu\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"57\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"255\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d573089534ca596e64efef474be7b6bc\"", +"\"Cartridge.Name\" \"Action Man\"", +"\"Cartridge.Note\" \"Uses paddle/joystick combination; variant to G.I. Joe-Cobra Strike by Parker Bros.\"", +"\"Display.YStart\" \"57\"", +"\"Controller.Left\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"157bddb7192754a45372be196797f284\"", +"\"Cartridge.Name\" \"Adventure\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2613 / 4975154\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Cartridge.Note\" \"One player joystick only\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ca4f8c5b4d6fb9d608bb96bc7ebd26c7\"", +"\"Cartridge.Name\" \"Adventures of Tron\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT4317\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"46\"", +"\"Display.Height\" \"180\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c4bc8c2e130d76346ebf8eb544991b46\"", +"\"Cartridge.Name\" \"Advertisement Cartridge\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"98e5e4d5c4dd9a986d30fd62bd2f75ae\"", +"\"Cartridge.Name\" \"Air-Sea Battle (1)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2602\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1d1d2603ec139867c1d1f5ddf83093f1\"", +"\"Cartridge.Name\" \"Air-Sea Battle (2)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2602\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"16cb43492987d2f32b423817cdaaf7c4\"", +"\"Cartridge.Name\" \"Air-Sea Battle (3)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2602\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4d77f291dca1518d7d8e47838695f54b\"", +"\"Cartridge.Name\" \"Airlock\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"DA 1004\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a9cb638cd2cb2e8e0643d7a67db4281c\"", +"\"Cartridge.Name\" \"Air Raiders (1)\"", +"\"Cartridge.Manufacturer\" \"M-Network\"", +"\"Cartridge.ModelNo\" \"MT5861\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"239\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"35be55426c1fec32dfb503b4f0651572\"", +"\"Cartridge.Name\" \"Air Raiders (2)\"", +"\"Cartridge.Manufacturer\" \"CCE\"", +"\"Cartridge.ModelNo\" \"C-817\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"239\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f1a0a23e6464d954e3a9579c4ccd01c8\"", +"\"Cartridge.Name\" \"Alien\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11006\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.XStart\" \"16\"", +"\"Display.Width\" \"128\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"103f1756d9dc0dd2b16b53ad0f0f1859\"", +"\"Cartridge.Name\" \"Alien's Return\"", +"\"Cartridge.Manufacturer\" \"ITT Family Games\"", +"\"Cartridge.ModelNo\" \"554-33391\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"214\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9e01f7f95cb8596765e03b9a36e8e33c\"", +"\"Cartridge.Name\" \"Alpha Beam with Ernie\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26103\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"acb7750b4d0c4bd34969802a7deb2990\"", +"\"Cartridge.Name\" \"Amidar\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5310\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Cartridge.Note\" \"One player joystick only\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"182\"", +"\"Console.LeftDifficulty\" \"A\"", +"\"Console.RightDifficulty\" \"A\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"038e1e79c3d4410defde4bfe0b99cc32\"", +"\"Cartridge.Name\" \"Aquaventure\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Cartridge.Type\" \"F8\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a7b584937911d60c120677fe0d47f36f\"", +"\"Cartridge.Name\" \"Armor Ambush\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5661\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c77c35a6fc3c0f12bf9e8bae48cba54b\"", +"\"Cartridge.Name\" \"Artillery Duel (1)\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"186\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3f039981255691d3859d04ef813a1264\"", +"\"Cartridge.Name\" \"Artillery Duel (2)\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"186\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"de78b3a064d374390ac0710f95edde92\"", +"\"Cartridge.Name\" \"Assault\"", +"\"Cartridge.Manufacturer\" \"Bomb\"", +"\"Cartridge.ModelNo\" \"CA281\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"203\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c5c7cc66febf2d4e743b4459de7ed868\"", +"\"Cartridge.Name\" \"Asterix\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2696\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"187\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b227175699e372b8fe10ce243ad6dda5\"", +"\"Cartridge.Name\" \"Asteroids (Copyright Version)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2649 / 4975163\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ccbd36746ed4525821a8083b0d6d2c2c\"", +"\"Cartridge.Name\" \"Asteroids\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2649\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"75169c08b56e4e6c36681e599c4d8cc5\"", +"\"Cartridge.Name\" \"Astroblast\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5666\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Uses left swapped paddles\"", +"\"Controller.Left\" \"Joystick\"", +"\"Controller.Right\" \"None\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8f53a3b925f0fd961d9b8c4d46ee6755\"", +"\"Cartridge.Name\" \"Astro War\"", +"\"Cartridge.Manufacturer\" \"Artic\"", +"\"Cartridge.ModelNo\" \"SM8002\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3f540a30fdee0b20aed7288e4a5ea528\"", +"\"Cartridge.Name\" \"Atari Video Cube\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2670\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9ad36e699ef6f45d9eb6c4cf90475c9f\"", +"\"Cartridge.Name\" \"Atlantis\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3203\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.YStart\" \"49\"", +"\"Display.Height\" \"185\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5b124850de9eea66781a50b2e9837000\"", +"\"Cartridge.Name\" \"Bachelor Party\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.ModelNo\" \"1002\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"274d17ccd825ef9c728d68394b4569d2\"", +"\"Cartridge.Name\" \"Bachelorette Party\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.ModelNo\" \"1004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8556b42aa05f94bc29ff39c39b11bff4\"", +"\"Cartridge.Name\" \"Backgammon\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2617 / 6699848 / 4975183\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"00ce0bdd43aed84a983bef38fe7f5ee3\"", +"\"Cartridge.Name\" \"Bank Heist\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11012\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"187\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f8240e62d8c0a64a61e19388414e3104\"", +"\"Cartridge.Name\" \"Barnstorming\"", +"\"Cartridge.Manufacturer\" \"Activision (Steve Cartwright)\"", +"\"Cartridge.ModelNo\" \"AX-013\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"819aeeb9a2e11deb54e6de334f843894\"", +"\"Cartridge.Name\" \"Basic Math / Fun With Numbers\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2661\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Released under two titles\"", +"\"Display.YStart\" \"22\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9f48eeb47836cf145a15771775f0767a\"", +"\"Cartridge.Name\" \"Basic Programming\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX262\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"Display.Height\" \"220\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d6dc9b4508da407e2437bfa4de53d1b2\"", +"\"Cartridge.Name\" \"Base Attack\"", +"\"Cartridge.Manufacturer\" \"Homevision\"", +"\"Cartridge.ModelNo\" \"13\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ab4ac994865fb16ebb85738316309457\"", +"\"Cartridge.Name\" \"Basketball\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2624\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"193\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e13c7627b2e136b9c449d9e8925b4547\"", +"\"Cartridge.Name\" \"Basketball (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2624\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"193\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"41f252a66c6301f1e8ab3612c19bc5d4\"", +"\"Cartridge.Name\" \"Battlezone\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2681\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F8\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.Name\" \"Beamrider (1)\"", +"\"Cartridge.MD5\" \"3604e725e81dd0abede07fd1c82eb058\"", +"\"Cartridge.Manufacturer\" \"Activision (Dave Rolle)\"", +"\"Cartridge.ModelNo\" \"AZ-037-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"79ab4123a83dc11d468fb2108ea09e2e\"", +"\"Cartridge.Name\" \"Beamrider (2)\"", +"\"Cartridge.Manufacturer\" \"Activision (Dave Rolle)\"", +"\"Cartridge.ModelNo\" \"AZ-037-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d0b9df57bfea66378c0418ec68cfe37f\"", +"\"Cartridge.Name\" \"Beany Bopper\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games (Sirius)\"", +"\"Cartridge.ModelNo\" \"11002\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ee6665683ebdb539e89ba620981cb0f6\"", +"\"Cartridge.Name\" \"Bearenstein Bears\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2658\"", +"\"Cartridge.Rarity\" \"Unbelieveably Rare\"", +"\"Cartridge.Type\" \"F8\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"59e96de9628e8373d1c685f5e57dcf10\"", +"\"Cartridge.Name\" \"Beat 'Em and Eat 'Em\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.ModelNo\" \"1003\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"073d7aff37b7601431e4f742c36c0dc1\"", +"\"Cartridge.Name\" \"Bermuda\"", +"\"Cartridge.Manufacturer\" \"Rainbow Vision\"", +"\"Cartridge.ModelNo\" \"SS-009\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"199\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b8ed78afdb1e6cfe44ef6e3428789d5f\"", +"\"Cartridge.Name\" \"Bermuda Triangle\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"112-007\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"136f75c4dd02c29283752b7e5799f978\"", +"\"Cartridge.Name\" \"Berzerk\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2650 / 4975168\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1278f74ca1dfaa9122df3eca3c5bcaad\"", +"\"Cartridge.Name\" \"Bi! Bi!\"", +"\"Cartridge.Manufacturer\" \"Rainbow Vision\"", +"\"Cartridge.Note\" \"Variant of Sea Hunt by Panda\"", +"\"Cartridge.ModelNo\" \"SS-013\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"245\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1802cc46b879b229272501998c5de04f\"", +"\"Cartridge.Name\" \"Big Bird's Egg Catch\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26104\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"One player left keypad only\"", +"\"Display.YStart\" \"27\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0a981c03204ac2b278ba392674682560\"", +"\"Cartridge.Name\" \"Blackjack\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2651 / 6699805 / 4975602\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"34\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"33d68c3cd74e5bc4cf0df3716c5848bc\"", +"\"Cartridge.Name\" \"Blueprint\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"4L-2486\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"968efc79d500dce52a906870a97358ab\"", +"\"Cartridge.Name\" \"BMX Airmaster\"", +"\"Cartridge.Manufacturer\" \"Atari / TNT Games\"", +"\"Cartridge.ModelNo\" \"CX26190\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2823364702595feea24a3fbee138a243\"", +"\"Cartridge.Name\" \"Bobby Is Going Home\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG206\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"245\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c59633dbebd926c150fb6d30b0576405\"", +"\"Cartridge.Name\" \"Bogey Blaster\"", +"\"Cartridge.Manufacturer\" \"Telegames\"", +"\"Cartridge.ModelNo\" \"5861 A030\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"14c2548712099c220964d7f044c59fd9\"", +"\"Cartridge.Name\" \"Boing\"", +"\"Cartridge.Manufacturer\" \"First Star Software\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a2aae759e4e76f85c8afec3b86529317\"", +"\"Cartridge.Name\" \"Boom Bang\"", +"\"Cartridge.Manufacturer\" \"Cooper Black\"", +"\"Cartridge.Note\" \"Variant of Crackpots by Activision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c9b7afad3bfd922e006a6bfc1d4f3fe7\"", +"\"Cartridge.Name\" \"Bowling\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2628 / 6699842 / 4975117\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d4aa6d6095258ce46aaf6f144b09eea7\"", +"\"Cartridge.Name\" \"Bowling (non-functional Taiwanese clone)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2628 / 6699842 / 4975117\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a28d872fc50fa6b64eb35981d0f4bb8d\"", +"\"Cartridge.Name\" \"Bowling (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2628 / 6699842 / 4975117\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c3ef5c4653212088eda54dc91d787870\"", +"\"Cartridge.Name\" \"Boxing\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AG-002\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"140\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1cca2197d95c5a41f2add49a13738055\"", +"\"Cartridge.Name\" \"Brain Games\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2664 / 6699818\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f34f08e5eb96e500e851a80be3277a56\"", +"\"Cartridge.Name\" \"Breakout / Breakaway IV\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2622\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e80a4026d29777c3c7993fbfaee8920f\"", +"\"Cartridge.Name\" \"Brick Kick\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cfd6a8b23d12b0462baf6a05ef347cd8\"", +"\"Cartridge.Name\" \"Bridge\"", +"\"Cartridge.Manufacturer\" \"Activision (Larry Kaplan)\"", +"\"Cartridge.ModelNo\" \"AX-006\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1cf59fc7b11cdbcefe931e41641772f6\"", +"\"Cartridge.Name\" \"Buck Rogers-Planet of Zoom\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"005-01\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fa4404fabc094e3a31fcd7b559cdd029\"", +"\"Cartridge.Name\" \"Bugs Bunny\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"68597264c8e57ada93be3a5be4565096\"", +"\"Cartridge.Name\" \"Bugs\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"DA 1005\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"76f53abbbf39a0063f24036d6ee0968a\"", +"\"Cartridge.Name\" \"Bump 'N' Jump\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT7045\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"E7\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0443cfa9872cdb49069186413275fa21\"", +"\"Cartridge.Name\" \"Burgertime\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT4518\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"E7\"", +"\"Display.YStart\" \"31\"", +"\"Display.Height\" \"180\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"19d6956ff17a959c48fcd8f4706a848d\"", +"\"Cartridge.Name\" \"Burning Desire\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7f6533386644c7d6358f871666c86e79\"", +"\"Cartridge.Name\" \"Cakewalk\"", +"\"Cartridge.Manufacturer\" \"CommaVid\"", +"\"Cartridge.ModelNo\" \"CM-008\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9ab72d3fd2cc1a0c9adb504502579037\"", +"\"Cartridge.Name\" \"California Games\"", +"\"Cartridge.Manufacturer\" \"Epyx\"", +"\"Cartridge.ModelNo\" \"8056100286\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"43\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3051b6071cb26377cd428af155e1bfc4\"", +"\"Cartridge.Name\" \"Canyon Bomber (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2607 / 6699828 / 4975115\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"feedcc20bc3ca34851cd5d9e38aa2ca6\"", +"\"Cartridge.Name\" \"Canyon Bomber\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2607 / 6699828 / 4975115\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"028024fb8e5e5f18ea586652f9799c96\"", +"\"Cartridge.Name\" \"Carnival\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2468\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"212\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b816296311019ab69a21cb9e9e235d12\"", +"\"Cartridge.Name\" \"Casino / Poker Plus\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2652\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"76f66ce3b83d7a104a899b4b3354a2f2\"", +"\"Cartridge.Name\" \"Cat & Mouse\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"33\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9e192601829f5f5c2d3b51f8ae25dbe5\"", +"\"Cartridge.Name\" \"Cathouse Blues\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"91c2098e88a6b13f977af8c003e0bca5\"", +"\"Cartridge.Name\" \"Centipede\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2676\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4311a4115fb7bc68477c96cf44cebacf\"", +"\"Cartridge.Name\" \"Challenge (1)\"", +"\"Cartridge.Manufacturer\" \"Zellers\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"213\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9905f9f4706223dadee84f6867ede8e3\"", +"\"Cartridge.Name\" \"Challenge (2)\"", +"\"Cartridge.Manufacturer\" \"Zellers\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"213\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5d799bfa9e1e7b6224877162accada0d\"", +"\"Cartridge.Name\" \"Challenge of...NEXAR\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-206\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3e33ac10dcf2dff014bc1decf8a9aea4\"", +"\"Cartridge.Name\" \"Chase The Chuckwagon\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.YStart\" \"22\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3f5a43602f960ede330cd2f43a25139e\"", +"\"Cartridge.Name\" \"Checkers\"", +"\"Cartridge.Manufacturer\" \"Activision (Alan Miller)\"", +"\"Cartridge.ModelNo\" \"AG-003\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.XStart\" \"8\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"749fec9918160921576f850b2375b516\"", +"\"Cartridge.Name\" \"China Syndrome\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-205\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"207\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c1cb228470a87beb5f36e90ac745da26\"", +"\"Cartridge.Name\" \"Chopper Command\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AX-015\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"49\"", +"\"Display.Height\" \"183\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3f58f972276d1e4e0e09582521ed7a5b\"", +"\"Cartridge.Name\" \"Chuck Norris Superkicks / Kung Fu Superkicks / Superkicks\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99003\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a29df35557f31dfea2e2ae4609c6ebb7\"", +"\"Cartridge.Name\" \"Circus Atari / Circus (Joystick Version)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2630 / 4975122\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a7b96a8150600b3e800a4689c3ec60a2\"", +"\"Cartridge.Name\" \"Circus Atari / Circus (Paddle Version)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2630 / 4975122\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Cartridge.Note\" \"Two player left paddles only\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"93acd5020ae8eb5673601e2edecbc158\"", +"\"Cartridge.Name\" \"Clock (version 3)\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7ff53f6922708119e7bf478d7d618c86\"", +"\"Cartridge.Name\" \"Clown Down Town\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1e587ca91518a47753a28217cd4fd586\"", +"\"Cartridge.Name\" \"Coconuts\"", +"\"Cartridge.Manufacturer\" \"Telesys\"", +"\"Cartridge.ModelNo\" \"1001\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"37\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5846b1d34c296bf7afc2fa05bbc16e98\"", +"\"Cartridge.Name\" \"Code Breaker\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2643\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"Display.YStart\" \"33\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"212d0b200ed8b45d8795ad899734d7d7\"", +"\"Cartridge.Name\" \"Coke Wins\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4093382187f8387e6d011883e8ea519b\"", +"\"Cartridge.Name\" \"Col 'N'\"", +"\"Cartridge.Manufacturer\" \"ITT Family Games\"", +"\"Cartridge.ModelNo\" \"554-33391\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"214\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ac05c0e53a5e7009ddd75ed4b99949fc\"", +"\"Cartridge.Name\" \"Combat / Tank Plus (1)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2601 / 6699801 / 4975124\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"206\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4c8832ed387bbafc055320c05205bc08\"", +"\"Cartridge.Name\" \"Combat / Tank Plus (2)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2601 / 6699801 / 4975124\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"206\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"be35d8b37bbc03848a5f020662a99909\"", +"\"Cartridge.Name\" \"Combat / Tank Plus (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2601 / 6699801 / 4975124\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"206\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"76a9bf05a6de8418a3ebc7fc254b71b4\"", +"\"Cartridge.Name\" \"Color Bar Generator\"", +"\"Cartridge.Manufacturer\" \"VideoSoft\"", +"\"Cartridge.ModelNo\" \"VS1008\"", +"\"Cartridge.Rarity\" \"Unreleased\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5d2cc33ca798783dee435eb29debf6d6\"", +"\"Cartridge.Name\" \"Commando\"", +"\"Cartridge.Manufacturer\" \"Activision (Mike Reidel)\"", +"\"Cartridge.ModelNo\" \"AK-043\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"180\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f457674cef449cfd85f21db2b4f631a7\"", +"\"Cartridge.Name\" \"Commando Raid\"", +"\"Cartridge.Manufacturer\" \"U.S. Games (Vidtec)\"", +"\"Cartridge.ModelNo\" \"VC 1004\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2c8835aed7f52a0da9ade5226ee5aa75\"", +"\"Cartridge.Name\" \"Communist Mutants from Space\"", +"\"Cartridge.ModelNo\" \"AR-4101\"", +"\"Cartridge.Manufacturer\" \"Starpath (Steve Landrum)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"196\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1f21666b8f78b65051b7a609f1d48608\"", +"\"Cartridge.Name\" \"Condor Attack\"", +"\"Cartridge.Manufacturer\" \"CCE\"", +"\"Cartridge.ModelNo\" \"C-851\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"00b7b4cbec81570642283e7fc1ef17af\"", +"\"Cartridge.Name\" \"Congo Bongo\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"006-01\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"57c5b351d4de021785cf8ed8191a195c\"", +"\"Cartridge.Name\" \"Cookie Monster Munch\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26102\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ab5bf1ef5e463ad1cbb11b6a33797228\"", +"\"Cartridge.Name\" \"Cosmic Ark\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3204\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"133b56de011d562cbab665968bde352b\"", +"\"Cartridge.Name\" \"Cosmic Commuter\"", +"\"Cartridge.Manufacturer\" \"Activision (John van Ryzin)\"", +"\"Cartridge.ModelNo\" \"AG-038\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f367e58667a30e7482175809e3cec4d4\"", +"\"Cartridge.Name\" \"Cosmic Corridor\"", +"\"Cartridge.Manufacturer\" \"Emag / Zimag\"", +"\"Cartridge.ModelNo\" \"GN-040 / 708-111\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"212\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3c853d864a1d5534ed0d4b325347f131\"", +"\"Cartridge.Name\" \"Cosmic Creeps\"", +"\"Cartridge.Manufacturer\" \"Telesys\"", +"\"Cartridge.ModelNo\" \"1002\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e5f17b3e62a21d0df1ca9aee1aa8c7c5\"", +"\"Cartridge.Name\" \"Cosmic Swarm\"", +"\"Cartridge.Manufacturer\" \"CommaVid\"", +"\"Cartridge.ModelNo\" \"CM-003\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9dec0be14d899e1aac4337acef5ab94a\"", +"\"Cartridge.Name\" \"Cosmic Swarm (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"CommaVid\"", +"\"Cartridge.ModelNo\" \"CM-003\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a184846d8904396830951217b47d13d9\"", +"\"Cartridge.Name\" \"Crackpots\"", +"\"Cartridge.Manufacturer\" \"Activision (Dan Kitchen)\"", +"\"Cartridge.ModelNo\" \"AX-029\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fb88c400d602fe759ae74ef1716ee84e\"", +"\"Cartridge.Name\" \"Crash Dive\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11031\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"55ef7b65066428367844342ed59f956c\"", +"\"Cartridge.Name\" \"Crazy Climber\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2683\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"46\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"48f18d69799a5f5451a5f0d17876acef\"", +"\"Cartridge.Name\" \"Criminal Pursuit\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8cd26dcf249456fe4aeb8db42d49df74\"", +"\"Cartridge.Name\" \"Crossbow\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26139\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c17bdc7d14a36e10837d039f43ee5fa3\"", +"\"Cartridge.Name\" \"Cross Force\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-203\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"74f623833429d35341b7a84bc09793c0\"", +"\"Cartridge.Name\" \"Cruise Missile\"", +"\"Cartridge.Manufacturer\" \"Froggo\"", +"\"Cartridge.ModelNo\" \"FG 1007\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"384f5fbf57b5e92ed708935ebf8a8610\"", +"\"Cartridge.Name\" \"Crypts of Chaos\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11009\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"185\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1c6eb740d3c485766cade566abab8208\"", +"\"Cartridge.Name\" \"Crystal Castles\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26110\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"50\"", +"\"Display.Height\" \"174\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6fa0ac6943e33637d8e77df14962fbfc\"", +"\"Cartridge.Name\" \"Cubicolor\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6caff1b4b96ed098123febf51f9121dc\"", +"\"Cartridge.Name\" \"Cubis (PAL version)\"", +"\"Cartridge.Manufacturer\" \"Eckhard Stolberg\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"Display.Format\" \"PAL\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"58513bae774360b96866a07ca0e8fd8e\"", +"\"Cartridge.Name\" \"Custer's Revenge\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.ModelNo\" \"1001\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"929e8a84ed50601d9af8c49b0425c7ea\"", +"\"Cartridge.Name\" \"Dancing Plate\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG205\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"230\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a422194290c64ef9d444da9d6a207807\"", +"\"Cartridge.Name\" \"Dark Cavern\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5667\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"189\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"106855474c69d08c8ffa308d47337269\"", +"\"Cartridge.Name\" \"Dark Chambers\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26151\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"48\"", +"\"Display.Height\" \"170\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"dba270850ae997969a18ee0001675821\"", +"\"Cartridge.Name\" \"Dark Mage (4K version)\"", +"\"Cartridge.Manufacturer\" \"Greg Troutman\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6333ef5b5cbb77acd47f558c8b7a95d3\"", +"\"Cartridge.Name\" \"Dark Mage (8K version)\"", +"\"Cartridge.Manufacturer\" \"Greg Troutman\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e4c00beb17fdc5881757855f2838c816\"", +"\"Cartridge.Name\" \"Deadly Duck\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games (Sirius)\"", +"\"Cartridge.ModelNo\" \"11004\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4e15ddfd48bca4f0bf999240c47b49f5\"", +"\"Cartridge.Name\" \"Deathtrap\"", +"\"Cartridge.Manufacturer\" \"Avalon Hill\"", +"\"Cartridge.ModelNo\" \"50010\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ac7c2260378975614192ca2bc3d20e0b\"", +"\"Cartridge.Name\" \"Decathlon\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AZ-030\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"FE\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0f643c34e40e3f1daafd9c524d3ffe64\"", +"\"Cartridge.Name\" \"Defender\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2609 / 4975186\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3a771876e4b61d42e3a3892ad885d889\"", +"\"Cartridge.Name\" \"Defender II\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26120\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"Variant of Stargate by Atari\"", +"\"Cartridge.Type\" \"F8SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d09935802d6760ae58253685ff649268\"", +"\"Cartridge.Name\" \"Demolition Herby\"", +"\"Cartridge.Manufacturer\" \"Telesys\"", +"\"Cartridge.ModelNo\" \"1006\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"187\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f0e0addc07971561ab80d9abe1b8d333\"", +"\"Cartridge.Name\" \"Demon Attack\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3200\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"698f569eab5a9906eec3bc7c6b3e0980\"", +"\"Cartridge.Name\" \"Demons!\"", +"\"Cartridge.Manufacturer\" \"SpkSoft\"", +"\"Cartridge.Note\" \"Variant of Phoenix by Atari and Demon Attack by Imagic\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f91fb8da3223b79f1c9a07b77ebfa0b2\"", +"\"Cartridge.Name\" \"Demons to Diamonds\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2615 / 4975140\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Two player left paddles only (swapped)\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"195\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fd4f5536fd80f35c64d365df85873418\"", +"\"Cartridge.Name\" \"Desert Falcon\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26140\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"17\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9222b25a0875022b412e8da37e7f6887\"", +"\"Cartridge.Name\" \"Dice Puzzle\"", +"\"Cartridge.Manufacturer\" \"Panda Inc.\"", +"\"Cartridge.ModelNo\" \"106\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6dda84fb8e442ecf34241ac0d1d91d69\"", +"\"Cartridge.Name\" \"Dig Dug\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2677\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"33\"", +"\"Display.Height\" \"193\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"939ce554f5c0e74cc6e4e62810ec2111\"", +"\"Cartridge.Name\" \"Dishaster\"", +"\"Cartridge.Manufacturer\" \"Cooper Black\"", +"\"Cartridge.Note\" \"Variant of Dancing Plate by Emag/Zimag\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"83bdc819980db99bf89a7f2ed6a2de59\"", +"\"Cartridge.Name\" \"Dodge 'Em / Dodger Cars\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2637 / 4975158\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"57\"", +"\"Display.Height\" \"180\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ca09fa7406b7d2aea10d969b6fc90195\"", +"\"Cartridge.Name\" \"Dolphin\"", +"\"Cartridge.Manufacturer\" \"Activision (Matthew Hubbard)\"", +"\"Cartridge.ModelNo\" \"AX-024\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"84d1cf884f029e458db196548db9c2ad\"", +"\"Cartridge.Name\" \"Domino (PAL version)\"", +"\"Cartridge.Manufacturer\" \"Eckhard Stolberg\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"Display.Format\" \"PAL\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"937736d899337036de818391a87271e0\"", +"\"Cartridge.Name\" \"Donald Duck's Speedboat\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26108\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"36b20c427975760cb9cf4a47e41369e4\"", +"\"Cartridge.Name\" \"Donkey Kong\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2451\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7fd52208fb6391bae0cd7e68c27bde6f\"", +"\"Cartridge.Name\" \"Donkey Kong Jr. (1)\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2653\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"35\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c8fa5d69d9e555eb16068ef87b1c9c45\"", +"\"Cartridge.Name\" \"Donkey Kong Jr. (2)\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2653\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"35\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"47464694e9cce07fdbfd096605bf39d4\"", +"\"Cartridge.Name\" \"Double Dragon\"", +"\"Cartridge.Manufacturer\" \"Activision (Dan Kitchen)\"", +"\"Cartridge.ModelNo\" \"AK-050-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"68\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"368d88a6c071caba60b4f778615aae94\"", +"\"Cartridge.Name\" \"Double Dunk\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26159\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6a882fb1413912d2ce5cf5fa62cf3875\"", +"\"Cartridge.Name\" \"Dragon Defender\"", +"\"Cartridge.ModelNo\" \"TP-605\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"249\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"41810dd94bd0de1110bedc5092bef5b0\"", +"\"Cartridge.Name\" \"Dragonfire\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3611\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"191\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"90ccf4f30a5ad8c801090b388ddd5613\"", +"\"Cartridge.Name\" \"Dragonstomper\"", +"\"Cartridge.ModelNo\" \"AR-4400\"", +"\"Cartridge.Manufacturer\" \"Starpath (Steve Landrum)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"189\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"77057d9d14b99e465ea9e29783af0ae3\"", +"\"Cartridge.Name\" \"Dragster\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AG-001\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1bb91bae919ddbd655fa25c54ea6f532\"", +"\"Cartridge.Name\" \"Duck Shoot\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"187\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1f773a94d919b2a3c647172bbb97f6b4\"", +"\"Cartridge.Name\" \"Dumbo's Flying Circus\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2678\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"51de328e79d919d7234cf19c1cd77fbc\"", +"\"Cartridge.Name\" \"Dukes of Hazard\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2678\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"033e21521e0bf4e54e8816873943406d\"", +"\"Cartridge.Name\" \"Earth Dies Screaming, The\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11020\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"42b2c3b4545f1499a083cfbc4a3b7640\"", +"\"Cartridge.Name\" \"Eggomania\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2003\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b6812eaf87127f043e78f91f2028f9f4\"", +"\"Cartridge.Name\" \"Eli's Ladder\"", +"\"Cartridge.Manufacturer\" \"Simage\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7eafc9827e8d5b1336905939e097aae7\"", +"\"Cartridge.Name\" \"Elk Attack\"", +"\"Cartridge.Manufacturer\" \"Mark R. Hahn\"", +"\"Cartridge.Rarity\" \"Uncommon / New Release\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"dbc8829ef6f12db8f463e30f60af209f\"", +"\"Cartridge.Name\" \"Encounter at L-5\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"DA 1001\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4279485e922b34f127a88904b31ce9fa\"", +"\"Cartridge.Name\" \"Enduro (1)\"", +"\"Cartridge.Manufacturer\" \"Activision (Larry Miller)\"", +"\"Cartridge.ModelNo\" \"AX-026\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.XStart\" \"12\"", +"\"Display.Width\" \"140\"", +"\"Display.YStart\" \"64\"", +"\"Display.Height\" \"175\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"94b92a882f6dbaa6993a46e2dcc58402\"", +"\"Cartridge.Name\" \"Enduro (2)\"", +"\"Cartridge.Manufacturer\" \"Activision (Larry Miller)\"", +"\"Cartridge.ModelNo\" \"AX-026\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.XStart\" \"12\"", +"\"Display.Width\" \"140\"", +"\"Display.YStart\" \"64\"", +"\"Display.Height\" \"175\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6b683be69f92958abe0e2a9945157ad5\"", +"\"Cartridge.Name\" \"Entombed / Name That Game\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2007 / VC 1007\"", +"\"Cartridge.Rarity\" \"Extremely Rare / Rare\"", +"\"Cartridge.Note\" \"Released as Name That Game for a contest (winning name was Entombed)\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d1a1841b7f2007a24439ac248374630a\"", +"\"Cartridge.Name\" \"Escape from the Mindmaster\"", +"\"Cartridge.ModelNo\" \"AR-4200\"", +"\"Cartridge.Manufacturer\" \"Starpath (Dennis Caswell)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"192\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f1127ade54037236e75a133b1dfc389d\"", +"\"Cartridge.Name\" \"Escape from the Mindmaster - Demonstration\"", +"\"Cartridge.ModelNo\" \"AR-4200\"", +"\"Cartridge.Manufacturer\" \"Starpath (Dennis Caswell)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f7a138eed69665b5cd1bfa796a550b01\"", +"\"Cartridge.Name\" \"Espial\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-012\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"3F\"", +"\"Display.YStart\" \"76\"", +"\"Display.Height\" \"196\"", +"", +"\"\"", +"", +"\"Cartridge.MD5\" \"615a3bf251a38eb6638cdc7ffbde5480\"", +"\"Cartridge.Name\" \"E.T.-The Extra Terrestrial\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2674\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6362396c8344eec3e86731a700b13abf\"", +"\"Cartridge.Name\" \"Exocet\"", +"\"Cartridge.Manufacturer\" \"Panda Inc.\"", +"\"Cartridge.ModelNo\" \"109\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2ac3a08cfbf1942ba169c3e9e6c47e09\"", +"\"Cartridge.Name\" \"F14 Tomcat Flight Simulator\"", +"\"Cartridge.Manufacturer\" \"Absolute Entertainment\"", +"\"Cartridge.ModelNo\" \"AK-046\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"56\"", +"\"Display.Height\" \"196\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b80d50ecee73919a507498d0a4d922ae\"", +"\"Cartridge.Name\" \"Fantastic Voyage\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11008\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f7e07080ed8396b68f2e5788a5c245e2\"", +"\"Cartridge.Name\" \"Farmyard Fun\"", +"\"Cartridge.ModelNo\" \"TP-617\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"212\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9de0d45731f90a0a922ab09228510393\"", +"\"Cartridge.Name\" \"Fast Eddie\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games (Sirius)\"", +"\"Cartridge.ModelNo\" \"11003\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"198\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"665b8f8ead0eef220ed53886fbd61ec9\"", +"\"Cartridge.Name\" \"Fast Food\"", +"\"Cartridge.Manufacturer\" \"Telesys\"", +"\"Cartridge.ModelNo\" \"1003\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"074ec425ec20579e64a7ded592155d48\"", +"\"Cartridge.Name\" \"Fatal Run\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26162\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Cartridge.Type\" \"F4SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"70\"", +"\"Display.Height\" \"178\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0b55399cf640a2a00ba72dd155a0c140\"", +"\"Cartridge.Name\" \"Fathom\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"O3205\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"211fbbdbbca1102dc5b43dc8157c09b3\"", +"\"Cartridge.Name\" \"Final Approach\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2009\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"386ff28ac5e254ba1b1bac6916bcc93a\"", +"\"Cartridge.Name\" \"Fireball\"", +"\"Cartridge.ModelNo\" \"AR-4300\"", +"\"Cartridge.Manufacturer\" \"Starpath (Scott Nelson)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"136\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"200\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d09f1830fb316515b90694c45728d702\"", +"\"Cartridge.Name\" \"Fire Fighter\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3400\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"20dca534b997bf607d658e77fbb3c0ee\"", +"\"Cartridge.Name\" \"Fire Fly\"", +"\"Cartridge.Manufacturer\" \"Mythicon\"", +"\"Cartridge.ModelNo\" \"MA-1002\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d3171407c3a8bb401a3a62eb578f48fb\"", +"\"Cartridge.Name\" \"Fire Spinner\"", +"\"Cartridge.Manufacturer\" \"Emag\"", +"\"Cartridge.ModelNo\" \"GN-080\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"01e60a109a6a67c70d3c0528381d0187\"", +"\"Cartridge.Name\" \"Fire Birds\"", +"\"Cartridge.Manufacturer\" \"ITT Family Games\"", +"\"Cartridge.ModelNo\" \"554-33383\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b8865f05676e64f3bec72b9defdacfa7\"", +"\"Cartridge.Name\" \"Fishing Derby\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AG-004\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"30512e0e83903fc05541d2f6a6a62654\"", +"\"Cartridge.Name\" \"Flag Capture\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2644\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"39\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8786c1e56ef221d946c64f6b65b697e9\"", +"\"Cartridge.Name\" \"Flash Gordon\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11015\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e549f1178e038fa88dc6d657dc441146\"", +"\"Cartridge.Name\" \"Football\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2625 / 6699827 / 4975114\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"213e5e82ecb42af237cfed8612c128ac\"", +"\"Cartridge.Name\" \"Forest\"", +"\"Cartridge.Manufacturer\" \"Panda Inc.\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"254\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"15dd21c2608e0d7d9f54c0d3f08cca1f\"", +"\"Cartridge.Name\" \"Frankenstein's Monster\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"112-008\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8e0ab801b1705a740b476b7f588c6d16\"", +"\"Cartridge.Name\" \"Freeway\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AG-009\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5f73e7175474c1c22fb8030c3158e9b3\"", +"\"Cartridge.Name\" \"Frog Pond\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2665\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"081e2c114c9c20b61acf25fc95c71bf4\"", +"\"Cartridge.Name\" \"Frogger\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5300\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"191\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"27c6a2ca16ad7d814626ceea62fa8fb4\"", +"\"Cartridge.Name\" \"Frogger II: Threedeep!\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5590\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c73ae5ba5a0a3f3ac77f0a9e14770e73\"", +"\"Cartridge.Name\" \"Official Frogger by Sega, The\"", +"\"Cartridge.ModelNo\" \"AR-4105\"", +"\"Cartridge.Manufacturer\" \"Starpath (Steve Landrum)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"33\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f5d103a9ae36d1d4ee7eef657b75d2b3\"", +"\"Cartridge.Name\" \"Official Frogger by Sega, The - Demonstration\"", +"\"Cartridge.ModelNo\" \"AR-4105\"", +"\"Cartridge.Manufacturer\" \"Starpath (Steve Landrum)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"33\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"dcc2956c7a39fdbf1e861fc5c595da0d\"", +"\"Cartridge.Name\" \"Frogs and Flies\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5664\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e556e07cc06c803f2955986f53ef63ed\"", +"\"Cartridge.Name\" \"Front Line\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2665\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4ca73eb959299471788f0b685c3ba0b5\"", +"\"Cartridge.Name\" \"Frostbite\"", +"\"Cartridge.Manufacturer\" \"Activision (Steve Cartwright)\"", +"\"Cartridge.ModelNo\" \"AX-031\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"01b09872dcd9556427761f0ed64aa42a\"", +"\"Cartridge.Name\" \"Galaga\"", +"\"Cartridge.Note\" \"Variant of River Raid by Activision (Carol Shaw)\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"199\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"211774f4c5739042618be8ff67351177\"", +"\"Cartridge.Name\" \"Galaxian\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2684\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"102672bbd7e25cd79f4384dd7214c32b\"", +"\"Cartridge.Name\" \"Game of Concentration, A / Hunt and Score / Memory Match\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2642 / 6699814\"", +"\"Cartridge.Rarity\" \"Rare (Uncommon) / Uncommon\"", +"\"Cartridge.Note\" \"Released under three titles\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"20edcc3aa6c189259fa7e2f044a99c49\"", +"\"Cartridge.Name\" \"Gangster Alley\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-201\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"728152f5ae6fdd0d3a9b88709bee6c7a\"", +"\"Cartridge.Name\" \"Gas Hog\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-217\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e64a8008812327853877a37befeb6465\"", +"\"Cartridge.Name\" \"Gauntlet\"", +"\"Cartridge.Manufacturer\" \"Answer Software\"", +"\"Cartridge.ModelNo\" \"ASC1002\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e314b42761cd13c03def744b4afc7b1b\"", +"\"Cartridge.Name\" \"Ghostbusters\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane and Dan Kitchen)\"", +"\"Cartridge.ModelNo\" \"AZ-108-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0eecb5f58f55de9db4eedb3a0f6b74a8\"", +"\"Cartridge.Name\" \"Ghost Manor (1)\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99002\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2bee7f226d506c217163bad4ab1768c0\"", +"\"Cartridge.Name\" \"Ghost Manor (2)\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99002\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c1fdd44efda916414be3527a47752c75\"", +"\"Cartridge.Name\" \"G.I. Joe-Cobra Strike\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5920\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Uses paddle/joystick combination\"", +"\"Display.YStart\" \"27\"", +"\"Controller.Left\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1c8c42d1aee5010b30e7f1992d69216e\"", +"\"Cartridge.Name\" \"Gigilo\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.ModelNo\" \"1009\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5e0c37f534ab5ccc4661768e2ddf0162\"", +"\"Cartridge.Name\" \"Glacier Patrol\"", +"\"Cartridge.Manufacturer\" \"Telegames (Sunrise)\"", +"\"Cartridge.ModelNo\" \"5667 A106\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2d9e5d8d083b6367eda880e80dfdfaeb\"", +"\"Cartridge.Name\" \"Glib\"", +"\"Cartridge.Manufacturer\" \"QDI\"", +"\"Cartridge.ModelNo\" \"87\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2e663eaa0d6b723b645e643750b942fd\"", +"\"Cartridge.Name\" \"Golf\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2634\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c16c79aad6272baffb8aae9a7fff0864\"", +"\"Cartridge.Name\" \"Gopher\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2001\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"81b3bf17cf01039d311b4cd738ae608e\"", +"\"Cartridge.Name\" \"Gorf\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"M8793\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2903896d88a341511586d69fcfc20f7d\"", +"\"Cartridge.Name\" \"Grand Prix\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AX-014\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"46\"", +"\"Display.Height\" \"189\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8ac18076d01a6b63acf6e2cab4968940\"", +"\"Cartridge.Name\" \"Gravitar\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2685\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"18f299edb5ba709a64c80c8c9cec24f2\"", +"\"Cartridge.Name\" \"Great Escape\"", +"\"Cartridge.Manufacturer\" \"Bomb\"", +"\"Cartridge.ModelNo\" \"CA282\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"220\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"01cb3e8dfab7203a9c62ba3b94b4e59f\"", +"\"Cartridge.Name\" \"Gremlins\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26127\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4ac9f40ddfcf194bd8732a75b3f2f214\"", +"\"Cartridge.Name\" \"Grover's Music Maker\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26106\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Cartridge.Note\" \"One player left keypad only\"", +"\"Display.YStart\" \"27\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7ab2f190d4e59e8742e76a6e870b567e\"", +"\"Cartridge.Name\" \"Guardian\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2008\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"\"Cartridge.MD5\" \"b311ab95e85bc0162308390728a7361d\"", +"\"Cartridge.Name\" \"Gyruss\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5080\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"180\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"30516cfbaa1bc3b5335ee53ad811f17a\"", +"\"Cartridge.Name\" \"Halloween\"", +"\"Cartridge.Manufacturer\" \"Wizard Video\"", +"\"Cartridge.ModelNo\" \"007\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b9232c1de494875efe1858fc8390616d\"", +"\"Cartridge.Name\" \"Harbor Escape\"", +"\"Cartridge.Manufacturer\" \"Panda Inc\"", +"\"Cartridge.ModelNo\" \"110\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f16c709df0a6c52f47ff52b9d95b7d8d\"", +"\"Cartridge.Name\" \"Hangman\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2662\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f0a6e99f5875891246c3dbecbf2d2cea\"", +"\"Cartridge.Name\" \"Haunted House\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2654 / 4975141\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fca4a5be1251927027f2c24774a02160\"", +"\"Cartridge.Name\" \"H.E.R.O.\"", +"\"Cartridge.Manufacturer\" \"Activision (John van Ryzin)\"", +"\"Cartridge.ModelNo\" \"AZ-036-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F8\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"49\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3d48b8b586a09bdbf49f1a016bf4d29a\"", +"\"Cartridge.Name\" \"Hole Hunter\"", +"\"Cartridge.ModelNo\" \"TP-606\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"220\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c52d9bbdc5530e1ef8e8ba7be692b01e\"", +"\"Cartridge.Name\" \"Holy Moley\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0bfabf1e98bdb180643f35f2165995d0\"", +"\"Cartridge.Name\" \"Home Run\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2623\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"42\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7972e5101fa548b952d852db24ad6060\"", +"\"Cartridge.Name\" \"Human Cannonball / Cannon Man\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2627 / 6699841\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f6a282374441012b01714e19699fc62a\"", +"\"Cartridge.Name\" \"I Want My Mommy\"", +"\"Cartridge.Manufacturer\" \"Emag / Zimag\"", +"\"Cartridge.ModelNo\" \"GN-010 / 710-111\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a4c08c4994eb9d24fb78be1793e82e26\"", +"\"Cartridge.Name\" \"Ice Hockey\"", +"\"Cartridge.Manufacturer\" \"Activision (Alan Miller)\"", +"\"Cartridge.ModelNo\" \"AX-012\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9813b9e4b8a6fd919c86a40c6bda8c93\"", +"\"Cartridge.Name\" \"Ikari Warriors\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26177\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9b21d8fc78cc4308990d99a4d906ec52\"", +"\"Cartridge.Name\" \"Immies and Aggies\"", +"\"Cartridge.Manufacturer\" \"CCE\"", +"\"Cartridge.ModelNo\" \"C-838\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3f6dbf448f25e2bd06dea44248eb122d\"", +"\"Cartridge.Name\" \"International Soccer\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5687\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b4030c38a720dd84b84178b6ce1fc749\"", +"\"Cartridge.Name\" \"International Soccer (Pirate version) (1)\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5687\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cd568d6acb2f14477ebf7e59fb382292\"", +"\"Cartridge.Name\" \"International Soccer (Pirate version) (2)\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5687\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"62\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c5301f549d0722049bb0add6b10d1e09\"", +"\"Cartridge.Name\" \"Indy 500 / Race\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2611 / 6699821 / 4975149\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Uses two steering controllers\"", +"\"Controller.Left\" \"Driving\"", +"\"Controller.Right\" \"Driving\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"afe88aae81d99e0947c0cfb687b16251\"", +"\"Cartridge.Name\" \"Infiltrate\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2006\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4868a81e1b6031ed66ecd60547e6ec85\"", +"\"Cartridge.Name\" \"Inv (1-3-98 version)\"", +"\"Cartridge.Manufacturer\" \"Eric Mooney and Piero Cavina\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cd139ae6d09f3665ad09eb79da3f9e49\"", +"\"Cartridge.Name\" \"Inv (4-24-97 version)\"", +"\"Cartridge.Manufacturer\" \"Eric Mooney\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4b9581c3100a1ef05eac1535d25385aa\"", +"\"Cartridge.Name\" \"IQ 180\"", +"\"Cartridge.Manufacturer\" \"Homevision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"235\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b9c3bc1d77f8e9d814735188bf324e40\"", +"\"Cartridge.Name\" \"James Bond 007\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5110\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"58a82e1da64a692fd727c25faef2ecc9\"", +"\"Cartridge.Name\" \"Jawbreaker\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-002\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"718ae62c70af4e5fd8e932fee216948a\"", +"\"Cartridge.Name\" \"Journey Escape\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"112-006\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3276c777cbe97cdd2b4a63ffc16b7151\"", +"\"Cartridge.Name\" \"Joust\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2691\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"36c29ceee2c151b23a1ad7aa04bd529d\"", +"\"Cartridge.Name\" \"Jr. Pac Man\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26123\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"49\"", +"\"Display.Height\" \"170\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2cccc079c15e9af94246f867ffc7e9bf\"", +"\"Cartridge.Name\" \"Jungle Fever\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.ModelNo\" \"1011\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2bb9f4686f7e08c5fcc69ec1a1c66fe7\"", +"\"Cartridge.Name\" \"Jungle Hunt\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2688\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5428cdfada281c569c74c7308c7f2c26\"", +"\"Cartridge.Name\" \"Kaboom!\"", +"\"Cartridge.Manufacturer\" \"Activision (Larry Kaplan)\"", +"\"Cartridge.ModelNo\" \"AG-010\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"2K\"", +"\"Cartridge.Note\" \"Two player left paddles only\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"192\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4326edb70ff20d0ee5ba58fa5cb09d60\"", +"\"Cartridge.Name\" \"Kangaroo\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2689\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cedbd67d1ff321c996051eec843f8716\"", +"\"Cartridge.Name\" \"Karate\"", +"\"Cartridge.Manufacturer\" \"Ultravision\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"be929419902e21bd7830a7a7d746195d\"", +"\"Cartridge.Name\" \"Keystone Kapers\"", +"\"Cartridge.Manufacturer\" \"Activision (Garry Kitchen)\"", +"\"Cartridge.ModelNo\" \"AX-025\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7a7f6ab9215a3a6b5940b8737f116359\"", +"\"Cartridge.Name\" \"Killer Satellites\"", +"\"Cartridge.ModelNo\" \"AR-4103\"", +"\"Cartridge.Manufacturer\" \"Starpath (Kevin Norman)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0b1056f1091cfdc5eb0e2301f47ac6c3\"", +"\"Cartridge.Name\" \"King Kong\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-001\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"210\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"eed9eaf1a0b6a2b9bc4c8032cb43e3fb\"", +"\"Cartridge.Name\" \"Klax (PAL version)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26192\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Format\" \"PAL\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2c29182edf0965a7f56fe0897d2f84ba\"", +"\"Cartridge.Name\" \"Klax\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26192\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7fcd1766de75c614a3ccc31b25dd5b7a\"", +"\"Cartridge.Name\" \"Knight on the Town / Lady In Wading\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"534e23210dd1993c828d944c6ac4d9fb\"", +"\"Cartridge.Name\" \"Kool-Aid Man\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT4648\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4baada22435320d185c95b7dd2bcdb24\"", +"\"Cartridge.Name\" \"Krull\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2682\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5b92a93b23523ff16e2789b820e2a4c5\"", +"\"Cartridge.Name\" \"Kung Fu Master\"", +"\"Cartridge.Manufacturer\" \"Activision (Dan Kitchen)\"", +"\"Cartridge.ModelNo\" \"AX-039\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"931b91a8ea2d39fe4dca1a23832b591a\"", +"\"Cartridge.Name\" \"Laser Blast\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AG-008\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"48287a9323a0ae6ab15e671ac2a87598\"", +"\"Cartridge.Name\" \"Laser Gates (1)\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1fa58679d4a39052bd9db059e8cda4ad\"", +"\"Cartridge.Name\" \"Laser Gates (2)\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4ab4af3adcdae8cdacc3d06084fc8d6a\"", +"\"Cartridge.Name\" \"Led Zepplin Demonstration\"", +"\"Cartridge.Manufacturer\" \"Nick Bensema\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"71464c54da46adae9447926fdbfc1abe\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.Name\" \"Lock 'n' Chase\"", +"\"Cartridge.ModelNo\" \"MT5663\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b4e2fd27d3180f0f4eb1065afc0d7fc9\"", +"\"Cartridge.Name\" \"London Blitz\"", +"\"Cartridge.Manufacturer\" \"Avalon Hill\"", +"\"Cartridge.ModelNo\" \"50010\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7c00e7a205d3fda98eb20da7c9c50a55\"", +"\"Cartridge.Name\" \"Lost Luggage\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2004\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"393e41ca8bdd35b52bf6256a968a9b89\"", +"\"Cartridge.Name\" \"M.A.D.\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 1012\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ccb5fa954fb76f09caae9a8c66462190\"", +"\"Cartridge.Name\" \"Malagai\"", +"\"Cartridge.Manufacturer\" \"Answer Software\"", +"\"Cartridge.ModelNo\" \"ASC1001\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"54a1c1255ed45eb8f71414dadb1cf669\"", +"\"Cartridge.Name\" \"Mangia\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-212\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"13895ef15610af0d0f89d588f376b3fe\"", +"\"Cartridge.Name\" \"Marauder\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-005\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b00e8217633e870bf39d948662a52aac\"", +"\"Cartridge.Name\" \"Marine Wars\"", +"\"Cartridge.Manufacturer\" \"Konami\"", +"\"Cartridge.ModelNo\" \"011\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1b8d35d93697450ea26ebf7ff17bd4d1\"", +"\"Cartridge.Name\" \"Marineflieger\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e908611d99890733be31733a979c62d8\"", +"\"Cartridge.Name\" \"Mario Bros.\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2697\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"835759ff95c2cdc2324d7c1e7c5fa237\"", +"\"Cartridge.Name\" \"M*A*S*H\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11011\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ae4be3a36b285c1a1dff202157e2155d\"", +"\"Cartridge.Name\" \"Master Builder\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-210\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3b76242691730b2dd22ec0ceab351bc6\"", +"\"Cartridge.Name\" \"Masters of the Universe: The Power of He-Man\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT4318\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"E7\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.XStart\" \"0\"", +"\"Display.Width\" \"160\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"185\"", +"\"TIA.PlayerDelay\" \"4\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"470878b9917ea0348d64b5750af149aa\"", +"\"Cartridge.Name\" \"Math Gran Prix\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2658 / 4975128\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8108ad2679bd055afec0a35a1dca46a4\"", +"\"Cartridge.Name\" \"Maze Craze / Maze Mania (1)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2635 / 4975157\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"175\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f825c538481f9a7a46d1e9bc06200aaf\"", +"\"Cartridge.Name\" \"Maze Craze / Maze Mania (2)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2635 / 4975157\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"175\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"daeb54957875c50198a7e616f9cc8144\"", +"\"Cartridge.Name\" \"Mega Force\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11005\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"318a9d6dda791268df92d72679914ac3\"", +"\"Cartridge.Name\" \"Megamania\"", +"\"Cartridge.Manufacturer\" \"Activision (Steve Cartwright)\"", +"\"Cartridge.ModelNo\" \"AX-017\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"43\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6522717cfd75d1dba252cbde76992090\"", +"\"Cartridge.Name\" \"Meteor Defense\"", +"\"Cartridge.Manufacturer\" \"ITT Family Games\"", +"\"Cartridge.ModelNo\" \"554-33391\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f1554569321dc933c87981cf5c239c43\"", +"\"Cartridge.Name\" \"Midnight Magic\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26129\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3c57748c8286cf9e821ecd064f21aaa9\"", +"\"Cartridge.Name\" \"Millipede\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26118\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f0541d2f7cda5ec7bab6d62b6128b823\"", +"\"Cartridge.Name\" \"Bionic Breakthrough\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Cartridge.Note\" \"One player Mind Link controller only\"", +"\"Controller.Left\" \"Mindlink\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fa0570561aa80896f0ead05c46351389\"", +"\"Cartridge.Name\" \"Miner 2049'er\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-008\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"3F\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"216\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"468f2dec984f3d4114ea84f05edf82b6\"", +"\"Cartridge.Name\" \"Miner 2049'er Volume II\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-011\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"3F\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"60\"", +"\"Display.Height\" \"212\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4543b7691914dfd69c3755a5287a95e1\"", +"\"Cartridge.Name\" \"Mines of Minos\"", +"\"Cartridge.Manufacturer\" \"CommaVid\"", +"\"Cartridge.ModelNo\" \"CM-005\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"635cc7a0db33773959d739d04eff96c2\"", +"\"Cartridge.Name\" \"Minesweeper\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"df62a658496ac98a3aa4a6ee5719c251\"", +"\"Cartridge.Name\" \"Miniature Golf\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2626\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3a2e2d0c6892aa14544083dfb7762782\"", +"\"Cartridge.Name\" \"Missile Command\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2638 / 4975166\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cb24210dc86d92df97b38cf2a51782da\"", +"\"Cartridge.Name\" \"Missile Control\"", +"\"Cartridge.Manufacturer\" \"Video Gems\"", +"\"Cartridge.ModelNo\" \"VG-01\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6efe876168e2d45d4719b6a61355e5fe\"", +"\"Cartridge.Name\" \"Mission 3000 A.D.\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG207\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"60\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7af40c1485ce9f29b1a7b069a2eb04a7\"", +"\"Cartridge.Name\" \"Mogul Maniac\"", +"\"Cartridge.Manufacturer\" \"Amiga\"", +"\"Cartridge.ModelNo\" \"3120\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1f60e48ad98b659a05ce0c1a8e999ad9\"", +"\"Cartridge.Name\" \"Mondo Pong\"", +"\"Cartridge.Manufacturer\" \"Piero Cavina\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6913c90002636c1487538d4004f7cac2\"", +"\"Cartridge.Name\" \"Monstercise\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26131\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"Keypad\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3347a6dd59049b15a38394aa2dafa585\"", +"\"Cartridge.Name\" \"Montezuma's Revenge: Starring Panama Joe\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5760\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"515046e3061b7b18aa3a551c3ae12673\"", +"\"Cartridge.Name\" \"Moon Patrol\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2692\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"175\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7db7c5fd8d3f53127a4bb0092c91d983\"", +"\"Cartridge.Name\" \"Moonsweeper (non-functional version)\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"O3207\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"203abb713c00b0884206dcc656caa48f\"", +"\"Cartridge.Name\" \"Moonsweeper\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"O3207\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f5a2f6efa33a3e5541bc680e9dc31d5b\"", +"\"Cartridge.Name\" \"Motocross Racer\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"45\"", +"\"Display.Height\" \"249\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b1e2d5dc1353af6d56cd2fe7cfe75254\"", +"\"Cartridge.Name\" \"Motorodeo\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26171\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7e51a58de2c0db7d33715f518893b0db\"", +"\"Cartridge.Name\" \"Mountain King\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"4L-2738\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"FASC\"", +"\"Display.XStart\" \"16\"", +"\"Display.Width\" \"128\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"186\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5678ebaa09ca3b699516dba4671643ed\"", +"\"Cartridge.Name\" \"Mouse Trap\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2459\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0164f26f6b38a34208cd4a2d0212afc3\"", +"\"Cartridge.Name\" \"Mr. Do!\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2656\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b7a7e34e304e4b7bc565ec01ba33ea27\"", +"\"Cartridge.Name\" \"Mr. Do!'s Castle\"", +"\"Cartridge.Manufacturer\" \"Coleco / Parker Bros.\"", +"\"Cartridge.ModelNo\" \"2695 / PB5820\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f0daaa966199ef2b49403e9a29d12c50\"", +"\"Cartridge.Name\" \"Mr. Postman\"", +"\"Cartridge.Manufacturer\" \"Bit Corp. / CCE\"", +"\"Cartridge.ModelNo\" \"PG209 / C-801\"", +"\"Cartridge.Rarity\" \"Rare / Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"87e79cd41ce136fd4f72cc6e2c161bee\"", +"\"Cartridge.Name\" \"Ms. Pac Man\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2675\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"079fe9103515d15bc108577e234a484d\"", +"\"Cartridge.Name\" \"Multi-Color Demo\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"65b106eba3e45f3dab72ea907f39f8b4\"", +"\"Cartridge.Name\" \"Music Machine\"", +"\"Cartridge.Manufacturer\" \"Sparrow\"", +"\"Cartridge.ModelNo\" \"GCG 1001T\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"dfad86dd85a11c80259f3ddb6151f48f\"", +"\"Cartridge.Name\" \"My Golf\"", +"\"Cartridge.Manufacturer\" \"Absolute Entertainment / Home Entertainment Suppliers\"", +"\"Cartridge.ModelNo\" \"535\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"53\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"392f00fd1a074a3c15bc96b0a57d52a1\"", +"\"Cartridge.Name\" \"Night Driver\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2633 / 4975119\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Note\" \"One player paddles only\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"None\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"", +"\"Cartridge.MD5\" \"f48022230bb774a7f22184b48a3385af\"", +"\"Cartridge.Name\" \"Night Driver (4K version)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2633 / 4975119\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Note\" \"One player paddles only, 4K version of 2K ROM\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"None\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2783006ee6519f15cbc96adae031c9a9\"", +"\"Cartridge.Name\" \"Night Stalker\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"61\"", +"\"Display.Height\" \"199\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ed0ab909cf7b30aff6fc28c3a4660b8e\"", +"\"Cartridge.Name\" \"Nightmare / Stuntman\"", +"\"Cartridge.Manufacturer\" \"Sancho / Panda Inc.\"", +"\"Cartridge.ModelNo\" \"TEC004 / 105\"", +"\"Cartridge.Rarity\" \"Extremely Rare / Rare\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b6d52a0cf53ad4216feb04147301f87d\"", +"\"Cartridge.Name\" \"No Escape!\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3312\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"de7a64108074098ba333cc0c70eef18a\"", +"\"Cartridge.Name\" \"Nuts\"", +"\"Cartridge.Manufacturer\" \"Technovision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"669840b0411bfbab5c05b786947d55d4\"", +"\"Cartridge.Name\" \"Obelix\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26117\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"64\"", +"\"Display.Height\" \"193\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4cabc895ea546022c2ecaa5129036634\"", +"\"Cartridge.Name\" \"Ocean City Defender\"", +"\"Cartridge.Manufacturer\" \"Zellers\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"36306070f0c90a72461551a7a4f3a209\"", +"\"Cartridge.Name\" \"Octopus\"", +"\"Cartridge.Manufacturer\" \"Zellers\"", +"\"Cartridge.ModelNo\" \"VC 1007\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"131864e1d18d3406048700d3c0760417\"", +"\"Cartridge.Name\" \"Off The Wall (1)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26168\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"98f63949e656ff309cefa672146dc1b8\"", +"\"Cartridge.Name\" \"Off The Wall (2)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26168\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b6166f15720fdf192932f1f76df5b65d\"", +"\"Cartridge.Name\" \"Off Your Rocker\"", +"\"Cartridge.Manufacturer\" \"Amiga\"", +"\"Cartridge.ModelNo\" \"3130\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c9c25fc536de9a7cdc5b9a916c459110\"", +"\"Cartridge.Name\" \"Oink!\"", +"\"Cartridge.Manufacturer\" \"Activision (Mike Lorenzen)\"", +"\"Cartridge.ModelNo\" \"AX-023\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ce4bbe11d682c15a490ae15a4a8716cf\"", +"\"Cartridge.Name\" \"Okie Dokie\"", +"\"Cartridge.Manufacturer\" \"RetroWare\"", +"\"Cartridge.Rarity\" \"Uncommon / New Release\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.XStart\" \"16\"", +"\"Display.Width\" \"128\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"178\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9947f1ebabb56fd075a96c6d37351efa\"", +"\"Cartridge.Name\" \"Omega Race\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"4L-2737\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Right difficulty 'A' to use Booster-Grip in both ports\"", +"\"Console.RightDifficulty\" \"A\"", +"\"Controller.Left\" \"Booster-Grip\"", +"\"Controller.Right\" \"Booster-Grip\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"28d5df3ed036ed63d33a31d0d8b85c47\"", +"\"Cartridge.Name\" \"Open Sesame\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG204\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fa1b060fd8e0bca0c2a097dcffce93d3\"", +"\"Cartridge.Name\" \"Oscar's Trash Race\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26101\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"One player keypad only\"", +"\"Controller.Left\" \"Keypad\"", +"\"Controller.Right\" \"None\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"55949cb7884f9db0f8dfcf8707c7e5cb\"", +"\"Cartridge.Name\" \"Othello\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2639 / 4975162\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"890c13590e0d8d5d6149737d930e4d95\"", +"\"Cartridge.Name\" \"Outlaw\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2605\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"91f0a708eeb93c133e9672ad2c8e0429\"", +"\"Cartridge.Name\" \"Oystron\"", +"\"Cartridge.Manufacturer\" \"Piero Cavina\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"936ef1d6f8a57b9ff575dc195ee36b80\"", +"\"Cartridge.Name\" \"Pac-Kong\"", +"\"Cartridge.Manufacturer\" \"Funvision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6e372f076fb9586aff416144f5cfe1cb\"", +"\"Cartridge.Name\" \"Pac-Man\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2646 / 4975185\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f8582bc6ca7046adb8e18164e8cecdbc\"", +"\"Cartridge.Name\" \"Panda Chase\"", +"\"Cartridge.Manufacturer\" \"Homevision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"249\"", +"\"Timer.Adjustment\" \"1\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"714e13c08508ee9a7785ceac908ae831\"", +"\"Cartridge.Name\" \"Parachute\"", +"\"Cartridge.Manufacturer\" \"Homevision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"220\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"012b8e6ef3b5fd5aabc94075c527709d\"", +"\"Cartridge.Name\" \"Party Mix\"", +"\"Cartridge.ModelNo\" \"AR-4302\"", +"\"Cartridge.Manufacturer\" \"Starpath (Dennis Caswell)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"0\"", +"\"Display.Width\" \"160\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4c0fb2544ae0f8b5f7ae8bce7bd7f134\"", +"\"Cartridge.Name\" \"Party Mix - Demonstration\"", +"\"Cartridge.ModelNo\" \"AR-4302\"", +"\"Cartridge.Manufacturer\" \"Starpath (Dennis Caswell)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"0\"", +"\"Display.Width\" \"160\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e40a818dac4dd851f3b4aafbe2f1e0c1\"", +"\"Cartridge.Name\" \"Peek-A-Boo\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26137\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ace319dc4f76548659876741a6690d57\"", +"\"Cartridge.Name\" \"Pele's Soccer / Pele's Championship Soccer\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2616\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7a09299f473105ae1ef3ad6f9f2cd807\"", +"\"Cartridge.Name\" \"Pele's Soccer / Pele's Championship Soccer (Pirate version)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2616\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4bcc7f6ba501a26ee785b7efbfb0fdc8\"", +"\"Cartridge.Name\" \"Pengo\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2690\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"196\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"09388bf390cd9a86dc0849697b96c7dc\"", +"\"Cartridge.Name\" \"Pete Rose Baseball\"", +"\"Cartridge.Manufacturer\" \"Absolute Entertainment\"", +"\"Cartridge.ModelNo\" \"AK-045\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6b1fc959e28bd71aed7b89014574bdc2\"", +"\"Cartridge.Name\" \"Phantom Tank\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG203\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"219\"", +"\"Display.YStart\" \"47\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"245\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"62f74a2736841191135514422b20382d\"", +"\"Cartridge.Name\" \"Pharoah's Curse\"", +"\"Cartridge.Manufacturer\" \"Technovision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"10\"", +"\"Display.Height\" \"200\"", +"\"Display.YStart\" \"22\"", +"\"Display.Height\" \"225\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7dcbfd2acc013e817f011309c7504daa\"", +"\"Cartridge.Name\" \"Phasor Patrol\"", +"\"Cartridge.ModelNo\" \"AR-4000\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ca54de69f7cdf4d7996e86f347129892\"", +"\"Cartridge.Name\" \"Philly Flasher\"", +"\"Cartridge.Manufacturer\" \"Mystique\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7e52a95074a66640fcfde124fffd491a\"", +"\"Cartridge.Name\" \"Phoenix\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2673\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"da79aad11572c80a96e261e4ac6392d0\"", +"\"Cartridge.Name\" \"Pick 'N' Pile\"", +"\"Cartridge.Manufacturer\" \"Salu\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"17c0a63f9a680e7a61beba81692d9297\"", +"\"Cartridge.Name\" \"Picnic\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d3423d7600879174c038f53e5ebbf9d3\"", +"\"Cartridge.Name\" \"Piece 'O Cake\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2005\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8e4fa8c6ad8d8dce0db8c991c166cdaa\"", +"\"Cartridge.Name\" \"Pigs In Space\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26114\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3e90cf23106f2e08b2781e41299de556\"", +"\"Cartridge.Name\" \"Pitfall!\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AX-018\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f73d2d0eff548e8fc66996f27acf2b4b\"", +"\"Cartridge.Name\" \"Pitfall! (CCE Version)\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AX-018\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f939780714db69dc69a80fbefe350e0d\"", +"\"Cartridge.Name\" \"Pitfall II: Lost Caverns\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AB-035-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"48eb1fcde4caf6a2dce059c98bd2e375\"", +"\"Cartridge.Name\" \"Pitfall II: Lost Caverns (10K version) (1)\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AB-035-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6d842c96d5a01967be9680080dd5be54\"", +"\"Cartridge.Name\" \"Pitfall II: Lost Caverns (10K version) (2)\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane)\"", +"\"Cartridge.ModelNo\" \"AB-035-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"043f165f384fbea3ea89393597951512\"", +"\"Cartridge.Name\" \"Planet Patrol\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-202\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"da4e3396aa2db3bd667f83a1cb9e4a36\"", +"\"Cartridge.Name\" \"Plaque Attack\"", +"\"Cartridge.Manufacturer\" \"Activision (Steve Cartwright)\"", +"\"Cartridge.ModelNo\" \"AX-027\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5f39353f7c6925779b0169a87ff86f1e\"", +"\"Cartridge.Name\" \"Pole Position\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2694\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"186\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"203049f4d8290bb4521cc4402415e737\"", +"\"Cartridge.Name\" \"Polaris\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-007\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"3F\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"62\"", +"\"Display.Height\" \"210\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ee28424af389a7f3672182009472500c\"", +"\"Cartridge.Name\" \"Polo\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4799a40b6e889370b7ee55c17ba65141\"", +"\"Cartridge.Name\" \"Pooyan\"", +"\"Cartridge.Manufacturer\" \"Gakken\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c7f13ef38f61ee2367ada94fdcc6d206\"", +"\"Cartridge.Name\" \"Popeye\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5370\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f93d7fee92717e161e6763a88a293ffa\"", +"\"Cartridge.Name\" \"Porky's\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11013\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"97d079315c09796ff6d95a06e4b70171\"", +"\"Cartridge.Name\" \"Pressure Cooker\"", +"\"Cartridge.Manufacturer\" \"Activision (Garry Kitchen)\"", +"\"Cartridge.ModelNo\" \"AZ-032\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ef3a4f64b6494ba770862768caf04b86\"", +"\"Cartridge.Name\" \"Private Eye\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AG-034-04\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"12123b534bdee79ed7563b9ad74f1cbd\"", +"\"Cartridge.Name\" \"Pro Wrestling\"", +"\"Cartridge.Manufacturer\" \"Activision / Absolute Entertainment\"", +"\"Cartridge.ModelNo\" \"AG-041\"", +"\"Cartridge.Rarity\" \"Extremely Rare / Rare\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"37fd7fa52d358f66984948999f1213c5\"", +"\"Cartridge.Name\" \"Pyramid War\"", +"\"Cartridge.Manufacturer\" \"Suntek\"", +"\"Cartridge.ModelNo\" \"4\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"484b0076816a104875e00467d431c2d2\"", +"\"Cartridge.Name\" \"Q*Bert\"", +"\"Cartridge.Manufacturer\" \"Parker Bros. / Atari\"", +"\"Cartridge.ModelNo\" \"PB5360 / CX26150\"", +"\"Cartridge.Rarity\" \"Common / Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"45\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"72b8dc752befbfb3ffda120eb98b2dd0\"", +"\"Cartridge.Name\" \"Q*Bert's Qubes (1)\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5550\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"210\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"517592e6e0c71731019c0cebc2ce044f\"", +"\"Cartridge.Name\" \"Q*Bert's Qubes (2)\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5550\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"210\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"392d34c0498075dd58df0ce7cd491ea2\"", +"\"Cartridge.Name\" \"Quadrun\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2686\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a0675883f9b09a3595ddd66a6f5d3498\"", +"\"Cartridge.Name\" \"Quest for Quinta Roo\"", +"\"Cartridge.Manufacturer\" \"Telegames (Sunrise)\"", +"\"Cartridge.ModelNo\" \"6057 A227\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7eba20c2291a982214cc7cbe8d0b47cd\"", +"\"Cartridge.Name\" \"Quick Step\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"O3211\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fb4ca865abc02d66e39651bd9ade140a\"", +"\"Cartridge.Name\" \"Rabbit Transit\"", +"\"Cartridge.ModelNo\" \"AR-4104\"", +"\"Cartridge.Manufacturer\" \"Starpath (Brian McGhie)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"198\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cd399bc422992a361ba932cc50f48b65\"", +"\"Cartridge.Name\" \"Rabbit Transit - Demonstration\"", +"\"Cartridge.ModelNo\" \"AR-4104\"", +"\"Cartridge.Manufacturer\" \"Starpath (Brian McGhie)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"198\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a20d931a8fddcd6f6116ed21ff5c4832\"", +"\"Cartridge.Name\" \"Racquetball\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2003\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"baf4ce885aa281fd31711da9b9795485\"", +"\"Cartridge.Name\" \"Radar Lock\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26176\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"185\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"92a1a605b7ad56d863a56373a866761b\"", +"\"Cartridge.Name\" \"Raft Rider\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2006\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f724d3dd2471ed4cf5f191dbb724b69f\"", +"\"Cartridge.Name\" \"Raiders of the Lost Ark\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2659\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7096a198531d3f16a99d518ac0d7519a\"", +"\"Cartridge.Name\" \"Ram It\"", +"\"Cartridge.Manufacturer\" \"Telesys\"", +"\"Cartridge.ModelNo\" \"1004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5e1b4629426f4992cf3b2905a696e1a7\"", +"\"Cartridge.Name\" \"Rampage!\"", +"\"Cartridge.Manufacturer\" \"Activision (Bobco)\"", +"\"Cartridge.ModelNo\" \"AK-049\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.XStart\" \"12\"", +"\"Display.Width\" \"136\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9f8fad4badcd7be61bbd2bcaeef3c58f\"", +"\"Cartridge.Name\" \"Reactor\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5330\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"eb634650c3912132092b7aee540bbce3\"", +"\"Cartridge.Name\" \"RealSports Baseball\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2640\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3177cc5c04c1a4080a927dfa4099482b\"", +"\"Cartridge.Name\" \"RealSports Boxing\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26135\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.Height\" \"185\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7ad257833190bc60277c1ca475057051\"", +"\"Cartridge.Name\" \"RealSports Football\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2668\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"08f853e8e01e711919e734d85349220d\"", +"\"Cartridge.Name\" \"RealSports Soccer\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2667\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c7eab66576696e11e3c11ffff92e13cc\"", +"\"Cartridge.Name\" \"RealSports Tennis (1)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2680\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"57\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"dac5c0fe74531f077c105b396874a9f1\"", +"\"Cartridge.Name\" \"RealSports Tennis (2)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2680\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"57\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"aed0b7bd64cc384f85fdea33e28daf3b\"", +"\"Cartridge.Name\" \"RealSports Volleyball\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2666\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8a9d874a38608964f33ec0c35cab618d\"", +"\"Cartridge.Name\" \"Rescue Bira Bira\"", +"\"Cartridge.Manufacturer\" \"Chris Cracknell / Mystique\"", +"\"Cartridge.Note\" \"Less adult-oriented version of Mystique's Jungle Fever\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"60a61da9b2f43dd7e13a5093ec41a53d\"", +"\"Cartridge.Name\" \"Rescue Terra I\"", +"\"Cartridge.Manufacturer\" \"Venture Vision\"", +"\"Cartridge.ModelNo\" \"VV2001\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4f64d6d0694d9b7a1ed7b0cb0b83e759\"", +"\"Cartridge.Name\" \"Revenge of the Beefsteak Tomatoes\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11016\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a995b6cbdb1f0433abc74050808590e6\"", +"\"Cartridge.Name\" \"Riddle of the Sphinx\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3600\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"185\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"31512cdfadfd82bfb6f196e3b0fd83cd\"", +"\"Cartridge.Name\" \"River Patrol\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"3F\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"393948436d1f4cc3192410bb918f9724\"", +"\"Cartridge.Name\" \"River Raid (1)\"", +"\"Cartridge.Manufacturer\" \"Activision (Carol Shaw)\"", +"\"Cartridge.ModelNo\" \"AX-020\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"199\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"291cc37604bc899e8e065c30153fc4b9\"", +"\"Cartridge.Name\" \"River Raid (2)\"", +"\"Cartridge.Manufacturer\" \"Activision (Carol Shaw)\"", +"\"Cartridge.ModelNo\" \"AX-020\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"199\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6ce2110ac5dd89ab398d9452891752ab\"", +"\"Cartridge.Name\" \"River Raid (Polyvox version)\"", +"\"Cartridge.Manufacturer\" \"Polyvox\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"199\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"dd92d6ad50976f881d86b52d38616118\"", +"\"Cartridge.Name\" \"River Raid (SpkSoft version)\"", +"\"Cartridge.Manufacturer\" \"SpkSoft\"", +"\"Cartridge.Note\" \"Variant of River Raid by Activision (Carol Shaw)\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ab56f1b2542a05bebc4fbccfc4803a38\"", +"\"Cartridge.Name\" \"River Raid II\"", +"\"Cartridge.Manufacturer\" \"Activision (Dan Kitchen / Imagineering)\"", +"\"Cartridge.ModelNo\" \"AK-048-04\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2bd00beefdb424fa39931a75e890695d\"", +"\"Cartridge.Name\" \"Road Runner\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2663\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"72a46e0c21f825518b7261c267ab886e\"", +"\"Cartridge.Name\" \"Robin Hood\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99005\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"25\"", +"\"Display.Height\" \"225\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1bef389e3dd2d4ca4f2f60d42c932509\"", +"\"Cartridge.Name\" \"Robot Fight\"", +"\"Cartridge.Manufacturer\" \"Homevision\"", +"\"Cartridge.ModelNo\" \"1\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4f618c2429138e0280969193ed6c107e\"", +"\"Cartridge.Name\" \"Robot Tank\"", +"\"Cartridge.Manufacturer\" \"Activision (Alan Miller)\"", +"\"Cartridge.ModelNo\" \"AZ-028\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"FE\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"67931b0d37dc99af250dd06f1c095e8d\"", +"\"Cartridge.Name\" \"Room of Doom\"", +"\"Cartridge.Manufacturer\" \"CommaVid\"", +"\"Cartridge.ModelNo\" \"CM-004\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"65bd29e8ab1b847309775b0de6b2e4fe\"", +"\"Cartridge.Name\" \"Roc 'n' Rope\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2667\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1ec57bbd27bdbd08b60c391c4895c1cf\"", +"\"Cartridge.Name\" \"Saboteur\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26119\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ed1a784875538c7871d035b7a98c2433\"", +"\"Cartridge.Name\" \"Save Our Ship\"", +"\"Cartridge.Manufacturer\" \"Technovision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"19e761e53e5ec8e9f2fceea62715ca06\"", +"\"Cartridge.Name\" \"Scuba Diver\"", +"\"Cartridge.Manufacturer\" \"Panda Inc.\"", +"\"Cartridge.ModelNo\" \"104\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"07f42847a79e4f5ae55cc03304b18c25\"", +"\"Cartridge.Name\" \"Sea Hawk\"", +"\"Cartridge.Manufacturer\" \"Froggo / Panda Inc. / Sancho\"", +"\"Cartridge.ModelNo\" \"FG 1008 / 108 / TEC002\"", +"\"Cartridge.Rarity\" \"Rare / Rare / Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"624e0a77f9ec67d628211aaf24d8aea6\"", +"\"Cartridge.Name\" \"Sea Hawk (Pirate version)\"", +"\"Cartridge.Manufacturer\" \"Froggo / Panda Inc. / Sancho\"", +"\"Cartridge.ModelNo\" \"FG 1008 / 108 / TEC002\"", +"\"Cartridge.Rarity\" \"Rare / Rare / Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5dccf215fdb9bbf5d4a6d0139e5e8bcb\"", +"\"Cartridge.Name\" \"Sea Hunt\"", +"\"Cartridge.Manufacturer\" \"Froggo\"", +"\"Cartridge.ModelNo\" \"FG 1009\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"", +"\"Cartridge.MD5\" \"68489e60268a5e6e052bad9c62681635\"", +"\"Cartridge.Name\" \"Sea Monster\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG201\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"259\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"240bfbac5163af4df5ae713985386f92\"", +"\"Cartridge.Name\" \"Seaquest\"", +"\"Cartridge.Manufacturer\" \"Activision (Steve Cartwright)\"", +"\"Cartridge.ModelNo\" \"AX-022\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fc24a94d4371c69bc58f5245ada43c44\"", +"\"Cartridge.Name\" \"Secret Quest\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26170\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.XStart\" \"16\"", +"\"Display.Width\" \"128\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"efffafc17b7cb01b9ca35324aa767364\"", +"\"Cartridge.Name\" \"Seesaw\"", +"\"Cartridge.Manufacturer\" \"Cooper Black\"", +"\"Cartridge.Note\" \"Variant of Circus Atari (Joystick Version) by Atari\"", +"\"Cartridge.ModelNo\" \"CX2630\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8da51e0c4b6b46f7619425119c7d018e\"", +"\"Cartridge.Name\" \"Sentinel\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26183\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"One player light gun only\"", +"\"Controller.Left\" \"Lightgun\"", +"\"Controller.Right\" \"None\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"46\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"54f7efa6428f14b9f610ad0ca757e26c\"", +"\"Cartridge.Name\" \"Shark Attack\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2005\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"22\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b5a1a189601a785bdb2f02a424080412\"", +"\"Cartridge.Name\" \"Shootin' Gallery\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3410\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"25b6dc012cdba63704ea9535c6987beb\"", +"\"Cartridge.Name\" \"Shuttle Orbiter\"", +"\"Cartridge.Manufacturer\" \"Avalon Hill\"", +"\"Cartridge.ModelNo\" \"50040\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ea38fcfc06ad87a0aed1a3d1588744e4\"", +"\"Cartridge.Name\" \"Sinistar\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26122\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7ead257e8b5a44cac538f5f54c7a0023\"", +"\"Cartridge.Name\" \"Sir Lancelot\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"7ead257e8b5a44cac538f5f54c7a0023\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f847fb8dba6c6d66d13724dbe5d95c4d\"", +"\"Cartridge.Name\" \"Skateboardin'\"", +"\"Cartridge.Manufacturer\" \"Activision (David Crane) / Absolute Entertainment\"", +"\"Cartridge.ModelNo\" \"AZ-042 / AG-042\"", +"\"Cartridge.Rarity\" \"Extremely Rare / Rare\"", +"\"Display.YStart\" \"34\"", +"\"Display.Height\" \"210\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"39c78d682516d79130b379fa9deb8d1c\"", +"\"Cartridge.Name\" \"Skeet Shoot\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 1001\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8654d7f0fb351960016e06646f639b02\"", +"\"Cartridge.Name\" \"Ski Hunt\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"235\"", +"\"Timer.Adjustment\" \"2\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f10e3f45fb01416c87e5835ab270b53a\"", +"\"Cartridge.Name\" \"Ski Run\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.ModelNo\" \"TP-607\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b76fbadc8ffb1f83e2ca08b6fb4d6c9f\"", +"\"Cartridge.Name\" \"Skiing\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AG-005\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"46c021a3e9e2fd00919ca3dd1a6b76d8\"", +"\"Cartridge.Name\" \"Sky Diver\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2629\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8bd8f65377023bdb7c5fcf46ddda5d31\"", +"\"Cartridge.Name\" \"Sky Jinks (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AG-019\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"2a0ba55e56e7a596146fa729acf0e109\"", +"\"Cartridge.Name\" \"Sky Jinks\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AG-019\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3b91c347d8e6427edbe942a7a405290d\"", +"\"Cartridge.Name\" \"Sky Skipper\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5350\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f90b5da189f24d7e1a2117d8c8abc952\"", +"\"Cartridge.Name\" \"Slot Machine\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2653\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"aed82052f7589df05a3f417bb4e45f0c\"", +"\"Cartridge.Name\" \"Slot Racers\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2606\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"34\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"24aff972d58990f9b88a6d787c796f1e\"", +"\"Cartridge.Name\" \"Smurf Rescue in Gargamel's Castle (PAL version)\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2465\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Format\" \"PAL\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3d1e83afdb4265fa2fb84819c9cfd39c\"", +"\"Cartridge.Name\" \"Smurf Rescue in Gargamel's Castle\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2465\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a204cd4fb1944c86e800120706512a64\"", +"\"Cartridge.Name\" \"Smurfs Save The Day\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2511\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"898b5467551d32af48a604802407b6e8\"", +"\"Cartridge.Name\" \"Snail Against Squirrel\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG208\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"245\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9c6faa4ff7f2ae549bbcb14f582b70e4\"", +"\"Cartridge.Name\" \"Sneak 'N' Peak\"", +"\"Cartridge.Manufacturer\" \"U.S. Games (Vidtec)\"", +"\"Cartridge.ModelNo\" \"VC 1002\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"229\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"57939b326df86b74ca6404f64f89fce9\"", +"\"Cartridge.Name\" \"Snoopy and the Red Baron\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26111\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"947317a89af38a49c4864d6bdd6a91fb\"", +"\"Cartridge.Name\" \"Solar Fox\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"4L-2487\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"97842fe847e8eb71263d6f92f7e122bd\"", +"\"Cartridge.Name\" \"Solar Storm\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"O3206\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e72eb8d4410152bdcb69e7fba327b420\"", +"\"Cartridge.Name\" \"Solaris\"", +"\"Cartridge.Manufacturer\" \"Atari (Douglas Neubauer)\"", +"\"Cartridge.ModelNo\" \"CX26136\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"193\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d2c4f8a4a98a905a9deef3ba7380ed64\"", +"\"Cartridge.Name\" \"Sorcerer\"", +"\"Cartridge.Manufacturer\" \"Mythicon\"", +"\"Cartridge.ModelNo\" \"MA-1001\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5f7ae9a7f8d79a3b37e8fc841f65643a\"", +"\"Cartridge.Name\" \"Sorcerer's Apprentice\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26109\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"17badbb3f54d1fc01ee68726882f26a6\"", +"\"Cartridge.Name\" \"Space Attack\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5659\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"559317712f989f097ea464517f1a8318\"", +"\"Cartridge.Name\" \"Space Canyon\"", +"\"Cartridge.Manufacturer\" \"Panda Inc.\"", +"\"Cartridge.ModelNo\" \"100\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"df6a28a89600affe36d94394ef597214\"", +"\"Cartridge.Name\" \"Space Cavern\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2002\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"72ffbef6504b75e69ee1045af9075f66\"", +"\"Cartridge.Name\" \"Space Invaders\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2632 / 4975153\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"012020625a3227815e47b37fd025e480\"", +"\"Cartridge.Name\" \"Space Invaders (alternate version)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2632\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6f2aaffaaf53d23a28bf6677b86ac0e3\"", +"\"Cartridge.Name\" \"Space Jockey\"", +"\"Cartridge.Manufacturer\" \"U.S. Games (Vidtec)\"", +"\"Cartridge.ModelNo\" \"VC 1001\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"45040679d72b101189c298a864a5b5ba\"", +"\"Cartridge.Name\" \"SpaceMaster X-7\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11022\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"1a624e236526c4c8f31175e9c89b2a22\"", +"\"Cartridge.Name\" \"Space Raid\"", +"\"Cartridge.Manufacturer\" \"Rainbow Vision\"", +"\"Cartridge.ModelNo\" \"SS-007\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3dfb7c1803f937fadc652a3e95ff7dc6\"", +"\"Cartridge.Name\" \"Space Robot\"", +"\"Cartridge.Manufacturer\" \"Artic\"", +"\"Cartridge.ModelNo\" \"SM8001\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5894c9c0c1e7e29f3ab86c6d3f673361\"", +"\"Cartridge.Name\" \"Space Shuttle\"", +"\"Cartridge.Manufacturer\" \"Activision (Steve Kitchen)\"", +"\"Cartridge.ModelNo\" \"AZ-033\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"df2745d585238780101df812d00b49f4\"", +"\"Cartridge.Name\" \"Space Tunnel (1)\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG202\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"259\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8917f7c1ac5eb05b82331cf01c495af2\"", +"\"Cartridge.Name\" \"Space Tunnel (2)\"", +"\"Cartridge.Manufacturer\" \"Bit Corp.\"", +"\"Cartridge.ModelNo\" \"PG202\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"259\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7e9da5cb84d5bc869854938fe3e85ffa\"", +"\"Cartridge.Name\" \"Space War (4K version)\"", +"\"Cartridge.Note\" \"4K version of 2K ROM\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2604\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a7ef44ccb5b9000caf02df3e6da71a92\"", +"\"Cartridge.Name\" \"Space War\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2604\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ec5c861b487a5075876ab01155e74c6c\"", +"\"Cartridge.Name\" \"Spacechase\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2001\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"24d018c4a6de7e5bd19a36f2b879b335\"", +"\"Cartridge.Name\" \"Spider Fighter\"", +"\"Cartridge.Manufacturer\" \"Activision (Larry Miller)\"", +"\"Cartridge.ModelNo\" \"AX-021\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"199eb0b8dce1408f3f7d46411b715ca9\"", +"\"Cartridge.Name\" \"Spider-Man\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5900\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"33\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"8454ed9787c9d8211748ccddb673e920\"", +"\"Cartridge.Name\" \"Spiderdroid\"", +"\"Cartridge.Manufacturer\" \"Froggo\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"182\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a4e885726af9d97b12bb5a36792eab63\"", +"\"Cartridge.Name\" \"Spike's Peak\"", +"\"Cartridge.Manufacturer\" \"Xonox\"", +"\"Cartridge.ModelNo\" \"99001\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cef2287d5fd80216b2200fb2ef1adfa8\"", +"\"Cartridge.Name\" \"Spitfire Attack\"", +"\"Cartridge.Manufacturer\" \"Milton Bradley\"", +"\"Cartridge.ModelNo\" \"4363\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4cd796b5911ed3f1062e805a3df33d98\"", +"\"Cartridge.Name\" \"Springer\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-006\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"3F\"", +"\"Display.YStart\" \"36\"", +"\"Display.Height\" \"202\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5a8afe5422abbfb0a342fb15afd7415f\"", +"\"Cartridge.Name\" \"Sprintmaster\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26155\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3105967f7222cc36a5ac6e5f6e89a0b4\"", +"\"Cartridge.Name\" \"Spy Hunter\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"011-02\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"200\"", +"\"TIA.PlayerDelay\" \"0\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ba257438f8a78862a9e014d831143690\"", +"\"Cartridge.Name\" \"Squeeze Box\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 2002\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"21a96301bb0df27fde2e7eefa49e0397\"", +"\"Cartridge.Name\" \"Sssnake\"", +"\"Cartridge.Manufacturer\" \"Data Age / Gameworld\"", +"\"Cartridge.ModelNo\" \"DA 1003 / DA 1002\"", +"\"Cartridge.Rarity\" \"Common / Extremely Rare\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"21d7334e406c2407e69dbddd7cec3583\"", +"\"Cartridge.Name\" \"Stampede\"", +"\"Cartridge.Manufacturer\" \"Activision (Bob Whitehead)\"", +"\"Cartridge.ModelNo\" \"AG-011\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"38bd172da8b2a3a176e517c213fcd5a6\"", +"\"Cartridge.Name\" \"Standalone Test Tape\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"MAO17600\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f526d0c519f5001adb1fc7948bfbb3ce\"", +"\"Cartridge.Name\" \"Star Fox\"", +"\"Cartridge.Manufacturer\" \"Mythicon\"", +"\"Cartridge.ModelNo\" \"MA-1003\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"54\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"57fa2d09c9e361de7bd2aa3a9575a760\"", +"\"Cartridge.Name\" \"Stargate (1)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26120\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"Variant of Defender II by Atari\"", +"\"Cartridge.Type\" \"F8SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0c48e820301251fbb6bcdc89bd3555d9\"", +"\"Cartridge.Name\" \"Stargate (2)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26120\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"Variant of Defender II by Atari\"", +"\"Cartridge.Type\" \"F8SC\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a3c1c70024d7aabb41381adbfb6d3b25\"", +"\"Cartridge.Name\" \"Star Gunner\"", +"\"Cartridge.Manufacturer\" \"Telesys\"", +"\"Cartridge.ModelNo\" \"1005\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cbd981a23c592fb9ab979223bb368cd5\"", +"\"Cartridge.Name\" \"Star Raiders\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2660 / 4975187\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Uses Joystick/Keypad combination\"", +"\"Controller.Right\" \"Keypad\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e363e467f605537f3777ad33e74e113a\"", +"\"Cartridge.Name\" \"Star Ship / Outer Space\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2603 / 6699803 / 4975601\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"79e5338dbfa6b64008bb0d72a3179d3c\"", +"\"Cartridge.Name\" \"Star Strike\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT4313\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"43\"", +"\"Display.Height\" \"193\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"03c3f7ba4585e349dd12bfa7b34b7729\"", +"\"Cartridge.Name\" \"Star Trek: Strategic Operations Simulator\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"004-01\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"813985a940aa739cc28df19e0edd4722\"", +"\"Cartridge.Name\" \"Star Voyager\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3201\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6339d28c9a7f92054e70029eb0375837\"", +"\"Cartridge.Name\" \"Star Wars: The Arcade Game\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5540\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5336f86f6b982cc925532f2e80aa1e17\"", +"\"Cartridge.Name\" \"Star Wars: Return of the Jedi, Death Star Battle\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5060\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3c8e57a246742fa5d59e517134c0b4e6\"", +"\"Cartridge.Name\" \"Star Wars: The Empire Strikes Back\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5050\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"41\"", +"\"Display.Height\" \"188\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c9f6e521a49a2d15dac56b6ddb3fb4c7\"", +"\"Cartridge.Name\" \"Star Wars: Jedi Arena\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5000\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"31\"", +"\"Display.Height\" \"199\"", +"\"Cartridge.Note\" \"Uses swapped paddles\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d69559f9c9dc6ef528d841bf9d91b275\"", +"\"Cartridge.Name\" \"Starmaster\"", +"\"Cartridge.Manufacturer\" \"Activision (Alan Miller)\"", +"\"Cartridge.ModelNo\" \"AX-016\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"656dc247db2871766dffd978c71da80c\"", +"\"Cartridge.Name\" \"Steeplechase\"", +"\"Cartridge.Manufacturer\" \"Atari (available with Sears label only)\"", +"\"Cartridge.ModelNo\" \"CX2614 / 4975126\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"198\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"47aef18509051bab493589cb2619170b\"", +"\"Cartridge.Name\" \"Stell-A-Sketch\"", +"\"Cartridge.Manufacturer\" \"Retroware\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"Cartridge.Note\" \"Uses two steering controllers\"", +"\"Controller.Left\" \"Driving\"", +"\"Controller.Right\" \"Driving\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0b8d3002d8f744a753ba434a4d39249a\"", +"\"Cartridge.Name\" \"Stellar Track\"", +"\"Cartridge.Manufacturer\" \"Atari (available with Sears label only)\"", +"\"Cartridge.ModelNo\" \"CX2619 / 4975159\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"136\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9333172e3c4992ecf548d3ac1f2553eb\"", +"\"Cartridge.Name\" \"Strategy X\"", +"\"Cartridge.Manufacturer\" \"Konami / Gakken\"", +"\"Cartridge.ModelNo\" \"010\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e10d2c785aadb42c06390fae0d92f282\"", +"\"Cartridge.Name\" \"Strawberry Shortcake Musical Match-Ups\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5910\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"396f7bc90ab4fa4975f8c74abe4e81f0\"", +"\"Cartridge.Name\" \"Street Racer / Speedway II\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2612 / 6699804 / 4975103\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Uses swapped paddles\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"Display.YStart\" \"34\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7b3cf0256e1fa0fdc538caf3d5d86337\"", +"\"Cartridge.Name\" \"Stronghold\"", +"\"Cartridge.Manufacturer\" \"CommaVid\"", +"\"Cartridge.ModelNo\" \"CM-009\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"93c52141d3c4e1b5574d072f1afde6cd\"", +"\"Cartridge.Name\" \"Subterranea\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"O3213\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"29\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5af9cd346266a1f2515e1fbc86f5186a\"", +"\"Cartridge.Name\" \"Sub Scan\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"002-01\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"46\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f3f5f72bfdd67f3d0e45d097e11b8091\"", +"\"Cartridge.Name\" \"Submarine Commander\"", +"\"Cartridge.Manufacturer\" \"Atari (available with Sears label only)\"", +"\"Cartridge.ModelNo\" \"CX2647 / 4975412\"", +"\"Cartridge.Rarity\" \"Extremely Rare Prototype / Extremely Rare\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e4c666ca0c36928b95b13d33474dbb44\"", +"\"Cartridge.Name\" \"Suicide Mission\"", +"\"Cartridge.ModelNo\" \"AR-4102\"", +"\"Cartridge.Manufacturer\" \"Starpath (Steve Hales and Steve Landrum)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"172\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"45027dde2be5bdd0cab522b80632717d\"", +"\"Cartridge.Name\" \"Summer Games\"", +"\"Cartridge.Manufacturer\" \"Epyx\"", +"\"Cartridge.ModelNo\" \"8056100250\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"F6\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7adbcf78399b19596671edbffc3d34aa\"", +"\"Cartridge.Name\" \"Super Baseball\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26152\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0ad9a358e361256b94f3fb4f2fa5a3b1\"", +"\"Cartridge.Name\" \"Super Breakout\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2608 / 4975165\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Cartridge.Note\" \"One player paddles only\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"136\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"180\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"None\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9d37a1be4a6e898026414b8fee2fc826\"", +"\"Cartridge.Name\" \"Super Challenge Baseball\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5658\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e275cbe7d4e11e62c3bfcfb38fca3d49\"", +"\"Cartridge.Name\" \"Super Challenge Football\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5658\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"197\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c29f8db680990cb45ef7fef6ab57a2c2\"", +"\"Cartridge.Name\" \"Super Cobra\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5320\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.Height\" \"206\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"724613effaf7743cbcd695fab469c2a8\"", +"\"Cartridge.Name\" \"Super Ferrari\"", +"\"Cartridge.Manufacturer\" \"Rainbow Vision\"", +"\"Cartridge.ModelNo\" \"SS-011\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"09abfe9a312ce7c9f661582fdf12eab6\"", +"\"Cartridge.Name\" \"Super Football\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26154\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"F6SC\"", +"\"Display.XStart\" \"4\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"182\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5de8803a59c36725888346fdc6e7429d\"", +"\"Cartridge.Name\" \"Superman (1)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2631 / 6699845 / 4975152\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a9531c763077464307086ec9a1fd057d\"", +"\"Cartridge.Name\" \"Superman (2)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2631 / 6699845 / 4975152\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"149b543c917c180a1b02d33c12415206\"", +"\"Cartridge.Name\" \"Superman (CCE Version)\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2631\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"aec9b885d0e8b24e871925630884095c\"", +"\"Cartridge.Name\" \"Surf's Up\"", +"\"Cartridge.Manufacturer\" \"Amiga\"", +"\"Cartridge.ModelNo\" \"3125\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"17\"", +"\"Display.Height\" \"244\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c20f15282a1aa8724d70c117e5c9709e\"", +"\"Cartridge.Name\" \"Surfer's Paradise: But Danger Below!\"", +"\"Cartridge.Manufacturer\" \"Video Gems\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4d7517ae69f95cfbc053be01312b7dba\"", +"\"Cartridge.Name\" \"Surround\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2641\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"045035f995272eb2deb8820111745a07\"", +"\"Cartridge.ModelNo\" \"AR-4401\"", +"\"Cartridge.Name\" \"Survival Island\"", +"\"Cartridge.Manufacturer\" \"Starpath (Scott Nelson)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Emulation.HmoveBlanks\" \"No\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"85e564dae5687e431955056fbda10978\"", +"\"Cartridge.Name\" \"Survival Run\"", +"\"Cartridge.Manufacturer\" \"Milton Bradley\"", +"\"Cartridge.ModelNo\" \"4362\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5ec73ac7d2ac95ac9530c6d33e713d14\"", +"\"Cartridge.Name\" \"Sweat!: The Decathlon Game\"", +"\"Cartridge.Manufacturer\" \"Starpath (Scott Nelson)\"", +"\"Cartridge.Rarity\" \"Unreleased Prototype\"", +"\"Cartridge.Type\" \"AR\"", +"\"Controller.Left\" \"Paddles\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"528400fad9a77fd5ad7fc5fdc2b7d69d\"", +"\"Cartridge.Name\" \"Sword of Saros\"", +"\"Cartridge.ModelNo\" \"AR-4201\"", +"\"Cartridge.Manufacturer\" \"Starpath (John Leupp)\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Cartridge.Type\" \"AR\"", +"\"Emulation.CPU\" \"High\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"05ebd183ea854c0a1b56c218246fbbae\"", +"\"Cartridge.Name\" \"SwordQuest EarthWorld\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2656\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f9d51a4e5f8b48f68770c89ffd495ed1\"", +"\"Cartridge.Name\" \"SwordQuest FireWorld\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2656\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"bc5389839857612cfabeb810ba7effdc\"", +"\"Cartridge.Name\" \"SwordQuest WaterWorld\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2671\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"294762000e853b4319f9991c1ced5dfc\"", +"\"Cartridge.Name\" \"T.F. Space Invaders\"", +"\"Cartridge.Type\" \"4K\"", +"\"Cartridge.Note\" \"Variant of Space Invaders by Atari/Sears\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d45ebf130ed9070ea8ebd56176e48a38\"", +"\"Cartridge.Name\" \"Tac-Scan\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"001-01\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Cartridge.Note\" \"One player right paddles only\"", +"\"Display.FrameRate\" \"60\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"152\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"202\"", +"\"Controller.Left\" \"None\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fa6fe97a10efb9e74c0b5a816e6e1958\"", +"\"Cartridge.Name\" \"Tanks But No Tanks\"", +"\"Cartridge.Manufacturer\" \"Emag / Zimag\"", +"\"Cartridge.ModelNo\" \"GN-030 / 707-111\"", +"\"Cartridge.Rarity\" \"Extremely Rare / Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"de3d0e37729d85afcb25a8d052a6e236\"", +"\"Cartridge.Name\" \"Tape Worm\"", +"\"Cartridge.Manufacturer\" \"Spectravision\"", +"\"Cartridge.ModelNo\" \"SA-204\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c0d2434348de72fa6edcc6d8e40f28d7\"", +"\"Cartridge.Name\" \"Tapper\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"010-01\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"32\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0c35806ff0019a270a7acae68de89d28\"", +"\"Cartridge.Name\" \"Task Force\"", +"\"Cartridge.Manufacturer\" \"Froggo\"", +"\"Cartridge.ModelNo\" \"FG 1003\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a1ead9c181d67859aa93c44e40f1709c\"", +"\"Cartridge.Name\" \"Tax Avoiders\"", +"\"Cartridge.Manufacturer\" \"American Videogame\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4702d8d9b48a332724af198aeac9e469\"", +"\"Cartridge.Name\" \"Taz\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2699\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"187\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"42cdd6a9e42a3639e190722b8ea3fc51\"", +"\"Cartridge.Name\" \"Tennis\"", +"\"Cartridge.Manufacturer\" \"Activision (Alan Miller)\"", +"\"Cartridge.ModelNo\" \"AG-007\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"39\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b0e1ee07fbc73493eac5651a52f90f00\"", +"\"Cartridge.Name\" \"Tetris26\"", +"\"Cartridge.Manufacturer\" \"Colin Hughes\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5c73693a89b06e5a09f1721a13176f95\"", +"\"Cartridge.Name\" \"Test Cartridge\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5eeb81292992e057b290a5cd196f155d\"", +"\"Cartridge.Name\" \"Texas Chainsaw Massacre\"", +"\"Cartridge.Manufacturer\" \"Wizard Video\"", +"\"Cartridge.ModelNo\" \"008\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a98b649912b6ca19eaf5c2d2faf38562\"", +"\"Cartridge.Name\" \"This Planet Sucks\"", +"\"Cartridge.Manufacturer\" \"What?Where\"", +"\"Cartridge.Rarity\" \"New Release\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e63a87c231ee9a506f9599aa4ef7dfb9\"", +"\"Cartridge.Name\" \"Threshold\"", +"\"Cartridge.Manufacturer\" \"Tigervision\"", +"\"Cartridge.ModelNo\" \"7-003\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"22\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cf507910d6e74568a68ac949537bccf9\"", +"\"Cartridge.Name\" \"Thunderground\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"003-01\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4e99ebd65a967cabf350db54405d577c\"", +"\"Cartridge.Name\" \"Time Pilot (1)\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2663\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fc2104dd2dadf9a6176c1c1c8f87ced9\"", +"\"Cartridge.Name\" \"Time Pilot (2)\"", +"\"Cartridge.Manufacturer\" \"Coleco\"", +"\"Cartridge.ModelNo\" \"2663\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"332f01fd18e99c6584f61aa45ee7791e\"", +"\"Cartridge.Name\" \"Time Warp\"", +"\"Cartridge.Manufacturer\" \"CCE\"", +"\"Cartridge.ModelNo\" \"C-845\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"Display.Height\" \"229\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"da6465a34d2e44d26aa9a2a0cd1bce4d\"", +"\"Cartridge.Name\" \"Title Match Pro Wrestling\"", +"\"Cartridge.Manufacturer\" \"Activision / Absolute Entertainment\"", +"\"Cartridge.ModelNo\" \"AG-041\"", +"\"Cartridge.Rarity\" \"Extremely Rare / Rare\"", +"\"Display.YStart\" \"47\"", +"\"Display.Height\" \"184\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ece908d77ab944f7bac84322b9973549\"", +"\"Cartridge.Name\" \"Tom Boy\"", +"\"Cartridge.Manufacturer\" \"Rainbow Vision\"", +"\"Cartridge.Note\" \"Variant of Pitfall by Activision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fa2be8125c3c60ab83e1c0fe56922fcb\"", +"\"Cartridge.Name\" \"Tooth Protectors\"", +"\"Cartridge.Manufacturer\" \"DSD / Camelot\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Cartridge.Type\" \"E0\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"0aa208060d7c140f20571e3341f5a3f8\"", +"\"Cartridge.Name\" \"Towering Inferno\"", +"\"Cartridge.Manufacturer\" \"U.S. Games\"", +"\"Cartridge.ModelNo\" \"VC 1009\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Note\" \"Uses swapped joysticks\"", +"\"Display.YStart\" \"32\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6ae4dc6d7351dacd1012749ca82f9a56\"", +"\"Cartridge.Name\" \"Track and Field\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26125 (label says CX26127 on some)\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4df9d7352a56a458abb7961bf10aba4e\"", +"\"Cartridge.Name\" \"Traffic\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"208\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"24df052902aa9de21c2b2525eb84a255\"", +"\"Cartridge.Name\" \"Trick Shot\"", +"\"Cartridge.Manufacturer\" \"Imagic\"", +"\"Cartridge.ModelNo\" \"IA3000\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"45\"", +"\"Display.Height\" \"177\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fb27afe896e7c928089307b32e5642ee\"", +"\"Cartridge.Name\" \"Tron Deadly Discs\"", +"\"Cartridge.Manufacturer\" \"M-Network (Mattel)\"", +"\"Cartridge.ModelNo\" \"MT5662\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"8\"", +"\"Display.Width\" \"144\"", +"\"Display.YStart\" \"53\"", +"\"Display.Height\" \"162\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"e17699a54c90f3a56ae4820f779f72c4\"", +"\"Cartridge.Name\" \"Tuby Bird\"", +"\"Cartridge.Manufacturer\" \"Rainbow Vision\"", +"\"Cartridge.Note\" \"Variant of Dolphin by Activision\"", +"\"Cartridge.ModelNo\" \"SS-020\"", +"\"Display.YStart\" \"44\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"b2737034f974535f5c0c6431ab8caf73\"", +"\"Cartridge.Name\" \"Tunnel Runner (1)\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"4L-2520\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"FASC\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"153\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"efefc02bbc5258815457f7a5b8d8750a\"", +"\"Cartridge.Name\" \"Tunnel Runner (2)\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"4L-2520\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Type\" \"FASC\"", +"\"Display.FrameRate\" \"20\"", +"\"Display.YStart\" \"67\"", +"\"Display.Height\" \"153\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7a5463545dfb2dcfdafa6074b2f2c15e\"", +"\"Cartridge.Name\" \"Turmoil\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games\"", +"\"Cartridge.ModelNo\" \"11007\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.FrameRate\" \"30\"", +"\"Display.YStart\" \"52\"", +"\"Display.Height\" \"186\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"085322bae40d904f53bdcc56df0593fc\"", +"\"Cartridge.Name\" \"Tutankham\"", +"\"Cartridge.Manufacturer\" \"Parker Bros.\"", +"\"Cartridge.ModelNo\" \"PB5340\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"619de46281eb2e0adbb98255732483b4\"", +"\"Cartridge.Name\" \"UFO Patrol\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"22\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"81a010abdba1a640f7adf7f84e13d307\"", +"\"Cartridge.Name\" \"Universal Chaos\"", +"\"Cartridge.Manufacturer\" \"Telegames\"", +"\"Cartridge.ModelNo\" \"7062 A305\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"42\"", +"\"Display.Height\" \"190\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a499d720e7ee35c62424de882a3351b6\"", +"\"Cartridge.Name\" \"Up 'n' Down\"", +"\"Cartridge.Manufacturer\" \"Sega\"", +"\"Cartridge.ModelNo\" \"009-01\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c6556e082aac04260596b4045bc122de\"", +"\"Cartridge.Name\" \"Vanguard\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2669\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"F8\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"3e899eba0ca8cd2972da1ae5479b4f0d\"", +"\"Cartridge.Name\" \"Venture\"", +"\"Cartridge.Manufacturer\" \"Coleco / Atari\"", +"\"Cartridge.ModelNo\" \"2457 / CX26145\"", +"\"Cartridge.Rarity\" \"Common / Rare\"", +"\"Display.YStart\" \"40\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"539d26b6e9df0da8e7465f0f5ad863b7\"", +"\"Cartridge.Name\" \"Video Checkers / Checkers\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2636 / 4975156\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"f0b7db930ca0e548c41a97160b9f6275\"", +"\"Cartridge.Name\" \"Video Chess\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2645 / 6699817 / 4975181\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.XStart\" \"16\"", +"\"Display.Width\" \"128\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"194\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4191b671bcd8237fc8e297b4947f2990\"", +"\"Cartridge.Name\" \"Video Jogger\"", +"\"Cartridge.Manufacturer\" \"Exus\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"200\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"60e0ea3cbe0913d39803477945e9e5ec\"", +"\"Cartridge.Name\" \"Video Olympics / Pong Sports\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2621\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Cartridge.Type\" \"2K\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"205\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"107cc025334211e6d29da0b6be46aec7\"", +"\"Cartridge.Name\" \"Video Pinball / Arcade Pinball\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2648 / 4975161\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Display.YStart\" \"35\"", +"\"Display.Height\" \"191\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ee659ae50e9df886ac4f8d7ad10d046a\"", +"\"Cartridge.Name\" \"Video Reflex\"", +"\"Cartridge.Manufacturer\" \"Exus\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"6041f400b45511aa3a69fab4b8fc8f41\"", +"\"Cartridge.Name\" \"Wabbit\"", +"\"Cartridge.Manufacturer\" \"Apollo\"", +"\"Cartridge.ModelNo\" \"AP 2010\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"38\"", +"\"Display.Height\" \"192\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d175258b2973b917a05b46df4e1cf15d\"", +"\"Cartridge.Name\" \"Walker\"", +"\"Cartridge.Note\" \"Variant of Clown Down Town\"", +"\"Display.Height\" \"219\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"d3456b4cf1bd1a7b8fb907af1a80ee15\"", +"\"Cartridge.Name\" \"Wall Ball\"", +"\"Cartridge.Manufacturer\" \"Avalon Hill\"", +"\"Cartridge.ModelNo\" \"50030\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c16fbfdbfdf5590cc8179e4b0f5f5aeb\"", +"\"Cartridge.Name\" \"Wall Defender\"", +"\"Cartridge.Manufacturer\" \"Home Entertainment Suppliers / Bomb\"", +"\"Cartridge.ModelNo\" \"CA285\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"219\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"cbe5a166550a8129a5e6d374901dffad\"", +"\"Cartridge.Name\" \"Warlords\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2610 / 4975127\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"Cartridge.Type\" \"4K\"", +"\"Display.YStart\" \"37\"", +"\"Display.Height\" \"194\"", +"\"Controller.Left\" \"Paddles\"", +"\"Controller.Right\" \"Paddles\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"679e910b27406c6a2072f9569ae35fc8\"", +"\"Cartridge.Name\" \"Warplock\"", +"\"Cartridge.Manufacturer\" \"Data Age\"", +"\"Cartridge.ModelNo\" \"DA 1002\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"9d2938eb2b17bb73e9a79bbc06053506\"", +"\"Cartridge.Name\" \"Wing War\"", +"\"Cartridge.Manufacturer\" \"Activision (Michael Greene)/ Imagic\"", +"\"Cartridge.ModelNo\" \"EIZ-002-04\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"200\"", +"\"Display.YStart\" \"52\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"83fafd7bd12e3335166c6314b3bde528\"", +"\"Cartridge.Name\" \"Winter Games\"", +"\"Cartridge.Manufacturer\" \"Epyx\"", +"\"Cartridge.ModelNo\" \"8056100251\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Display.YStart\" \"37\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7b24bfe1b61864e758ada1fe9adaa098\"", +"\"Cartridge.Name\" \"Wizard\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.Rarity\" \"Unreleased Prototype\"", +"\"Display.Height\" \"195\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"7e8aa18bc9502eb57daaf5e7c1e94da7\"", +"\"Cartridge.Name\" \"Wizard of Wor\"", +"\"Cartridge.Manufacturer\" \"CBS Electronics\"", +"\"Cartridge.ModelNo\" \"M8774\"", +"\"Cartridge.Rarity\" \"Rare\"", +"\"Cartridge.Note\" \"Uses swapped joysticks\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"ec3beb6d8b5689e867bafb5d5f507491\"", +"\"Cartridge.Name\" \"Word Zapper\"", +"\"Cartridge.Manufacturer\" \"U.S. Games (Vidtec)\"", +"\"Cartridge.ModelNo\" \"VC 1003\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"87f020daa98d0132e98e43db7d8fea7e\"", +"\"Cartridge.Name\" \"Worm War I\"", +"\"Cartridge.Manufacturer\" \"20th Century Fox Video Games (Sirius)\"", +"\"Cartridge.ModelNo\" \"11001\"", +"\"Cartridge.Rarity\" \"Uncommon\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5961d259115e99c30b64fe7058256bcf\"", +"\"Cartridge.Name\" \"X-Man\"", +"\"Cartridge.Manufacturer\" \"Universal Gamex\"", +"\"Cartridge.ModelNo\" \"GX-001\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare\"", +"\"Display.YStart\" \"27\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"eaf744185d5e8def899950ba7c6e7bb5\"", +"\"Cartridge.Name\" \"Xenophobe\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX26172\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c6688781f4ab844852f4e3352772289b\"", +"\"Cartridge.Name\" \"Xevious\"", +"\"Cartridge.Manufacturer\" \"Atari\"", +"\"Cartridge.ModelNo\" \"CX2695\"", +"\"Cartridge.Rarity\" \"Unbelievably Rare Prototype\"", +"\"Display.Height\" \"210\"", +"\"Display.YStart\" \"26\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c5930d0e8cdae3e037349bfa08e871be\"", +"\"Cartridge.Name\" \"Yars' Revenge (1)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2655 / 4975167\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"193\"", +"\"Display.YStart\" \"36\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"5f681403b1051a0822344f467b05a94d\"", +"\"Cartridge.Name\" \"Yars' Revenge (2)\"", +"\"Cartridge.Manufacturer\" \"Atari / Sears\"", +"\"Cartridge.ModelNo\" \"CX2655 / 4975167\"", +"\"Cartridge.Rarity\" \"Common\"", +"\"Display.Height\" \"193\"", +"\"Display.YStart\" \"36\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"c469151655e333793472777052013f4f\"", +"\"Cartridge.Name\" \"Z-Tack\"", +"\"Cartridge.Manufacturer\" \"Bomb\"", +"\"Cartridge.ModelNo\" \"13\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.YStart\" \"30\"", +"\"Display.Height\" \"205\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"eea0da9b987d661264cce69a7c13c3bd\"", +"\"Cartridge.Name\" \"Zaxxon\"", +"\"Cartridge.Manufacturer\" \"Coleco / CCE / CBS Electronics\"", +"\"Cartridge.ModelNo\" \"2454 / C-1001 / 4L-2277\"", +"\"Cartridge.Rarity\" \"Uncommon / Extremely Rare / Extremely Rare\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"fb833ed50c865a9a505a125fc9d79a7e\"", +"\"Cartridge.Name\" \"Zoo Fun\"", +"\"Cartridge.Manufacturer\" \"Homevision\"", +"\"Cartridge.Rarity\" \"Extremely Rare\"", +"\"Display.Height\" \"249\"", +"\"Display.YStart\" \"27\"", +"\"Timer.Adjustment\" \"1\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"4565c1a7abce773e53c75b35414adefd\"", +"\"Cartridge.Name\" \"Non-functional STARPATH.BIN\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"130c5742cd6cbe4877704d733d5b08ca\"", +"\"Cartridge.Name\" \"Non-functional WORLDEND.BIN\"", +"\"\"", +"", +"\"Cartridge.MD5\" \"a8916734ff8c64ec3342f4c73fd5b57d\"", +"\"Cartridge.Name\" \"Non-functional SALTDIAG.BIN\"", diff --git a/stella/src/build/M6502Hi.ins b/stella/src/build/M6502Hi.ins new file mode 100644 index 000000000..bdd98ce8c --- /dev/null +++ b/stella/src/build/M6502Hi.ins @@ -0,0 +1,3194 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Hi.ins,v 1.1.1.1 2001-12-27 19:54:16 bwmott Exp $ +//============================================================================ + +/** + Code to handle addressing modes and branch instructions for + high compatibility emulation + + @author Bradford W. Mott + @version $Id: M6502Hi.ins,v 1.1.1.1 2001-12-27 19:54:16 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Hi.ins,v 1.1.1.1 2001-12-27 19:54:16 bwmott Exp $ +//============================================================================ + +/** + Code and cases to emulate each of the 6502 instruction + + @author Bradford W. Mott + @version $Id: M6502Hi.ins,v 1.1.1.1 2001-12-27 19:54:16 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +case 0x69: +{ + operand = peek(PC++); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x65: +{ + operand = peek(peek(PC++)); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x75: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x6D: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x7D: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x79: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x61: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x71: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + + + +case 0x29: +{ + operand = peek(PC++); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x25: +{ + operand = peek(peek(PC++)); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x35: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x2D: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x3D: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x39: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x21: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x31: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x0a: +{ + peek(PC); +} +{ + // Set carry flag according to the left-most bit in A + C = A & 0x80; + + A <<= 1; + + notZ = A; + N = A & 0x80; +} +break; + +case 0x06: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x16: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x0e: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x1e: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x0f: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x1f: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x1b: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x07: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x17: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x03: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + operandAddress = peek(pointer++); + operandAddress |= ((uInt16)peek(pointer) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x13: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x8f: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); +} +{ + poke(operandAddress, A & X); +} +break; + +case 0x87: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, A & X); +} +break; + +case 0x97: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + Y) & 0xFF; +} +{ + poke(operandAddress, A & X); +} +break; + +case 0x83: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + operandAddress = peek(pointer++); + operandAddress |= ((uInt16)peek(pointer) << 8); +} +{ + poke(operandAddress, A & X); +} +break; + + +case 0x90: +{ + operand = peek(PC++); +} +{ + if(!C) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0xb0: +{ + operand = peek(PC++); +} +{ + if(C) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0xf0: +{ + operand = peek(PC++); +} +{ + if(!notZ) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0x24: +{ + operand = peek(peek(PC++)); +} +{ + notZ = (A & operand); + N = operand & 0x80; + V = operand & 0x40; +} +break; + +case 0x2C: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + notZ = (A & operand); + N = operand & 0x80; + V = operand & 0x40; +} +break; + + +case 0x30: +{ + operand = peek(PC++); +} +{ + if(N) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0xD0: +{ + operand = peek(PC++); +} +{ + if(notZ) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0x10: +{ + operand = peek(PC++); +} +{ + if(!N) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0x00: +{ + peek(PC++); + poke(0x0100 + SP--, PC >> 8); + poke(0x0100 + SP--, PC & 0x00ff); + poke(0x0100 + SP--, PS()); + + B = true; + I = true; + + PC = peek(0xfffe); + PC |= ((uInt16)peek(0xffff) << 8); +} +break; + + +case 0x50: +{ + operand = peek(PC++); +} +{ + if(!V) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0x70: +{ + operand = peek(PC++); +} +{ + if(V) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +} +break; + + +case 0x18: +{ + peek(PC); +} +{ + C = false; +} +break; + + +case 0xd8: +{ + peek(PC); +} +{ + D = false; +} +break; + + +case 0x58: +{ + peek(PC); +} +{ + I = false; +} +break; + + +case 0xb8: +{ + peek(PC); +} +{ + V = false; +} +break; + + +case 0xc9: +{ + operand = peek(PC++); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xc5: +{ + operand = peek(peek(PC++)); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xd5: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xcd: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xdd: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xd9: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xc1: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xd1: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + + +case 0xe0: +{ + operand = peek(PC++); +} +{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xe4: +{ + operand = peek(peek(PC++)); +} +{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xec: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + + +case 0xc0: +{ + operand = peek(PC++); +} +{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xc4: +{ + operand = peek(peek(PC++)); +} +{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xcc: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + + +case 0xc6: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xd6: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xce: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xde: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + + +case 0xca: +{ + peek(PC); +} +{ + X--; + + notZ = X; + N = X & 0x80; +} +break; + + +case 0x88: +{ + peek(PC); +} +{ + Y--; + + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0x49: +{ + operand = peek(PC++); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x45: +{ + operand = peek(peek(PC++)); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x55: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x4d: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x5d: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x59: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x41: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x51: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0xe6: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xf6: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xee: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xfe: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + + +case 0xe8: +{ + peek(PC); +} +{ + X++; + notZ = X; + N = X & 0x80; +} +break; + + +case 0xc8: +{ + peek(PC); +} +{ + Y++; + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0x4c: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); +} +{ + PC = operandAddress; +} +break; + +case 0x6c: +{ + uInt16 addr = peek(PC++); + addr |= ((uInt16)peek(PC++) << 8); + + // Simulate the error in the indirect addressing mode! + uInt16 high = NOTSAMEPAGE(addr, addr + 1) ? (addr & 0xff00) : (addr + 1); + + operandAddress = peek(addr); + operandAddress |= ((uInt16)peek(high) << 8); +} +{ + PC = operandAddress; +} +break; + + +case 0x20: +{ + uInt8 low = peek(PC++); + peek(0x0100 + SP); + + // It seems that the 650x does not push the address of the next instruction + // on the stack it actually pushes the address of the next instruction + // minus one. This is compensated for in the RTS instruction + poke(0x0100 + SP--, PC >> 8); + poke(0x0100 + SP--, PC & 0xff); + + PC = low | ((uInt16)peek(PC++) << 8); +} +break; + + +case 0xa9: +{ + operand = peek(PC++); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xa5: +{ + operand = peek(peek(PC++)); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xb5: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xad: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xbd: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xb9: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xa1: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xb1: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0xa2: +{ + operand = peek(PC++); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xa6: +{ + operand = peek(peek(PC++)); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xb6: +{ + uInt8 address = peek(PC++); + peek(address); + address += Y; + operand = peek(address); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xae: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xbe: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + + +case 0xa0: +{ + operand = peek(PC++); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xa4: +{ + operand = peek(peek(PC++)); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xb4: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xac: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xbc: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0x4a: +{ + peek(PC); +} +{ + // Set carry flag according to the right-most bit + C = A & 0x01; + + A = (A >> 1) & 0x7f; + + notZ = A; + N = A & 0x80; +} +break; + + +case 0x46: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x56: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x4e: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x5e: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x1a: +case 0x3a: +case 0x5a: +case 0x7a: +case 0xda: +case 0xfa: +case 0xea: +{ + peek(PC); +} +{ +} +break; + +case 0x80: +case 0x82: +case 0xc2: +case 0xe2: +{ + operand = peek(PC++); +} +{ +} +break; + +case 0x04: +case 0x44: +case 0x64: +{ + operand = peek(peek(PC++)); +} +{ +} +break; + +case 0x14: +case 0x34: +case 0x54: +case 0x74: +case 0xd4: +case 0xf4: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ +} +break; + +case 0x0c: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ +} +break; + +case 0x1c: +case 0x3c: +case 0x5c: +case 0x7c: +case 0xdc: +case 0xfc: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ +} +break; + + +case 0x09: +{ + operand = peek(PC++); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x05: +{ + operand = peek(peek(PC++)); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x15: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x0D: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x1D: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x19: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x01: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x11: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x48: +{ + peek(PC); +} +{ + poke(0x0100 + SP--, A); +} +break; + + +case 0x08: +{ + peek(PC); +} +{ + poke(0x0100 + SP--, PS()); +} +break; + + +case 0x68: +{ + peek(PC); +} +{ + peek(0x0100 + SP++); + A = peek(0x0100 + SP); + notZ = A; + N = A & 0x80; +} +break; + + +case 0x28: +{ + peek(PC); +} +{ + peek(0x0100 + SP++); + PS(peek(0x0100 + SP)); +} +break; + + +case 0x2f: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x3f: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x3b: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x27: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x37: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x23: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + operandAddress = peek(pointer++); + operandAddress |= ((uInt16)peek(pointer) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x33: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x2a: +{ + peek(PC); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit + C = A & 0x80; + + A = (A << 1) | (oldC ? 1 : 0); + + notZ = A; + N = A & 0x80; +} +break; + + +case 0x26: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x36: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x2e: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x3e: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x6a: +{ + peek(PC); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = A & 0x01; + + A = ((A >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + + notZ = A; + N = A & 0x80; +} +break; + +case 0x66: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x76: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x6e: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x7e: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x40: +{ + peek(PC); +} +{ + peek(0x0100 + SP++); + PS(peek(0x0100 + SP++)); + PC = peek(0x0100 + SP++); + PC |= ((uInt16)peek(0x0100 + SP) << 8); +} +break; + + +case 0x60: +{ + peek(PC); +} +{ + peek(0x0100 + SP++); + PC = peek(0x0100 + SP++); + PC |= ((uInt16)peek(0x0100 + SP) << 8); + peek(PC++); +} +break; + + +case 0xe9: +{ + operand = peek(PC++); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xe5: +{ + operand = peek(peek(PC++)); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xf5: +{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xed: +{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xfd: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xf9: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xe1: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xf1: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + + +case 0x38: +{ + peek(PC); +} +{ + C = true; +} +break; + + +case 0xf8: +{ + peek(PC); +} +{ + D = true; +} +break; + + +case 0x78: +{ + peek(PC); +} +{ + I = true; +} +break; + + +case 0x85: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, A); +} +break; + +case 0x95: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; +} +{ + poke(operandAddress, A); +} +break; + +case 0x8d: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); +} +{ + poke(operandAddress, A); +} +break; + +case 0x9d: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; +} +{ + poke(operandAddress, A); +} +break; + +case 0x99: +{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; +} +{ + poke(operandAddress, A); +} +break; + +case 0x81: +{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + operandAddress = peek(pointer++); + operandAddress |= ((uInt16)peek(pointer) << 8); +} +{ + poke(operandAddress, A); +} +break; + +case 0x91: +{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; +} +{ + poke(operandAddress, A); +} +break; + + +case 0x86: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, X); +} +break; + +case 0x96: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + Y) & 0xFF; +} +{ + poke(operandAddress, X); +} +break; + +case 0x8e: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); +} +{ + poke(operandAddress, X); +} +break; + + +case 0x84: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, Y); +} +break; + +case 0x94: +{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; +} +{ + poke(operandAddress, Y); +} +break; + +case 0x8c: +{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); +} +{ + poke(operandAddress, Y); +} +break; + + +case 0xaa: +{ + peek(PC); +} +{ + X = A; + notZ = X; + N = X & 0x80; +} +break; + + +case 0xa8: +{ + peek(PC); +} +{ + Y = A; + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0xba: +{ + peek(PC); +} +{ + X = SP; + notZ = X; + N = X & 0x80; +} +break; + + +case 0x8a: +{ + peek(PC); +} +{ + A = X; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x9a: +{ + peek(PC); +} +{ + SP = X; +} +break; + + +case 0x98: +{ + peek(PC); +} +{ + A = Y; + notZ = A; + N = A & 0x80; +} +break; + + diff --git a/stella/src/build/M6502Low.ins b/stella/src/build/M6502Low.ins new file mode 100644 index 000000000..ec6ead77a --- /dev/null +++ b/stella/src/build/M6502Low.ins @@ -0,0 +1,3188 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Low.ins,v 1.1.1.1 2001-12-27 19:54:17 bwmott Exp $ +//============================================================================ + +/** + Code to handle addressing modes and branch instructions for + low compatibility emulation + + @author Bradford W. Mott + @version $Id: M6502Low.ins,v 1.1.1.1 2001-12-27 19:54:17 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Low.ins,v 1.1.1.1 2001-12-27 19:54:17 bwmott Exp $ +//============================================================================ + +/** + Code and cases to emulate each of the 6502 instruction + + @author Bradford W. Mott + @version $Id: M6502Low.ins,v 1.1.1.1 2001-12-27 19:54:17 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +case 0x69: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x65: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x75: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x6D: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x7D: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x79: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x61: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0x71: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + + + +case 0x29: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x25: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x35: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x2D: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x3D: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x39: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x21: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x31: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A &= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x0a: +{ +} +{ + // Set carry flag according to the left-most bit in A + C = A & 0x80; + + A <<= 1; + + notZ = A; + N = A & 0x80; +} +break; + +case 0x06: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x16: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x0e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x1e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x0f: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x1f: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x1b: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += Y; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x07: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x17: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x03: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x13: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operandAddress += Y; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x8f: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; +} +{ + poke(operandAddress, A & X); +} +break; + +case 0x87: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, A & X); +} +break; + +case 0x97: +{ + operandAddress = (uInt8)(peek(PC++) + Y); +} +{ + poke(operandAddress, A & X); +} +break; + +case 0x83: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); +} +{ + poke(operandAddress, A & X); +} +break; + + +case 0x90: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(!C) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0xb0: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(C) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0xf0: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(!notZ) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0x24: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + notZ = (A & operand); + N = operand & 0x80; + V = operand & 0x40; +} +break; + +case 0x2C: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + notZ = (A & operand); + N = operand & 0x80; + V = operand & 0x40; +} +break; + + +case 0x30: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(N) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0xD0: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(notZ) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0x10: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(!N) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0x00: +{ + peek(PC++); + poke(0x0100 + SP--, PC >> 8); + poke(0x0100 + SP--, PC & 0x00ff); + poke(0x0100 + SP--, PS()); + + B = true; + I = true; + + PC = peek(0xfffe); + PC |= ((uInt16)peek(0xffff) << 8); +} +break; + + +case 0x50: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(!V) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0x70: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + if(V) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +} +break; + + +case 0x18: +{ +} +{ + C = false; +} +break; + + +case 0xd8: +{ +} +{ + D = false; +} +break; + + +case 0x58: +{ +} +{ + I = false; +} +break; + + +case 0xb8: +{ +} +{ + V = false; +} +break; + + +case 0xc9: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xc5: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xd5: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xcd: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xdd: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xd9: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xc1: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xd1: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + + +case 0xe0: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xe4: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xec: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + + +case 0xc0: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xc4: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + +case 0xcc: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +} +break; + + +case 0xc6: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xd6: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xce: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xde: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + + +case 0xca: +{ +} +{ + X--; + + notZ = X; + N = X & 0x80; +} +break; + + +case 0x88: +{ +} +{ + Y--; + + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0x49: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x45: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x55: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x4d: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x5d: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x59: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x41: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x51: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A ^= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0xe6: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xf6: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xee: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + +case 0xfe: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +} +break; + + +case 0xe8: +{ +} +{ + X++; + notZ = X; + N = X & 0x80; +} +break; + + +case 0xc8: +{ +} +{ + Y++; + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0x4c: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; +} +{ + PC = operandAddress; +} +break; + +case 0x6c: +{ + uInt16 addr = peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // Simulate the error in the indirect addressing mode! + uInt16 high = NOTSAMEPAGE(addr, addr + 1) ? (addr & 0xff00) : (addr + 1); + + operandAddress = peek(addr) | ((uInt16)peek(high) << 8); +} +{ + PC = operandAddress; +} +break; + + +case 0x20: +{ + uInt8 low = peek(PC++); + peek(0x0100 + SP); + + // It seems that the 650x does not push the address of the next instruction + // on the stack it actually pushes the address of the next instruction + // minus one. This is compensated for in the RTS instruction + poke(0x0100 + SP--, PC >> 8); + poke(0x0100 + SP--, PC & 0xff); + + PC = low | ((uInt16)peek(PC++) << 8); +} +break; + + +case 0xa9: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xa5: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xb5: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xad: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xbd: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xb9: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xa1: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0xb1: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A = operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0xa2: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xa6: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xb6: +{ + operandAddress = (uInt8)(peek(PC++) + Y); + operand = peek(operandAddress); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xae: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + +case 0xbe: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + X = operand; + notZ = X; + N = X & 0x80; +} +break; + + +case 0xa0: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xa4: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xb4: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xac: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + +case 0xbc: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + Y = operand; + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0x4a: +{ +} +{ + // Set carry flag according to the right-most bit + C = A & 0x01; + + A = (A >> 1) & 0x7f; + + notZ = A; + N = A & 0x80; +} +break; + + +case 0x46: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x56: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x4e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x5e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x1a: +case 0x3a: +case 0x5a: +case 0x7a: +case 0xda: +case 0xfa: +case 0xea: +{ +} +{ +} +break; + +case 0x80: +case 0x82: +case 0xc2: +case 0xe2: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ +} +break; + +case 0x04: +case 0x44: +case 0x64: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ +} +break; + +case 0x14: +case 0x34: +case 0x54: +case 0x74: +case 0xd4: +case 0xf4: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ +} +break; + +case 0x0c: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ +} +break; + +case 0x1c: +case 0x3c: +case 0x5c: +case 0x7c: +case 0xdc: +case 0xfc: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ +} +break; + + +case 0x09: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x05: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x15: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x0D: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x1D: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x19: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x01: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + +case 0x11: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + A |= operand; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x48: +{ +} +{ + poke(0x0100 + SP--, A); +} +break; + + +case 0x08: +{ +} +{ + poke(0x0100 + SP--, PS()); +} +break; + + +case 0x68: +{ +} +{ + peek(0x0100 + SP++); + A = peek(0x0100 + SP); + notZ = A; + N = A & 0x80; +} +break; + + +case 0x28: +{ +} +{ + peek(0x0100 + SP++); + PS(peek(0x0100 + SP)); +} +break; + + +case 0x2f: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x3f: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x3b: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x27: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x37: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x23: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + +case 0x33: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x2a: +{ +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit + C = A & 0x80; + + A = (A << 1) | (oldC ? 1 : 0); + + notZ = A; + N = A & 0x80; +} +break; + + +case 0x26: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x36: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x2e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x3e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x6a: +{ +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = A & 0x01; + + A = ((A >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + + notZ = A; + N = A & 0x80; +} +break; + +case 0x66: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x76: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x6e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + +case 0x7e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +} +{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +} +break; + + +case 0x40: +{ +} +{ + peek(0x0100 + SP++); + PS(peek(0x0100 + SP++)); + PC = peek(0x0100 + SP++); + PC |= ((uInt16)peek(0x0100 + SP) << 8); +} +break; + + +case 0x60: +{ +} +{ + peek(0x0100 + SP++); + PC = peek(0x0100 + SP++); + PC |= ((uInt16)peek(0x0100 + SP) << 8); + peek(PC++); +} +break; + + +case 0xe9: +{ + operandAddress = PC++; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xe5: +{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xf5: +{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xed: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xfd: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xf9: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xe1: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + +case 0xf1: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +} +{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +} +break; + + +case 0x38: +{ +} +{ + C = true; +} +break; + + +case 0xf8: +{ +} +{ + D = true; +} +break; + + +case 0x78: +{ +} +{ + I = true; +} +break; + + +case 0x85: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, A); +} +break; + +case 0x95: +{ + operandAddress = (uInt8)(peek(PC++) + X); +} +{ + poke(operandAddress, A); +} +break; + +case 0x8d: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; +} +{ + poke(operandAddress, A); +} +break; + +case 0x9d: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; +} +{ + poke(operandAddress, A); +} +break; + +case 0x99: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += Y; +} +{ + poke(operandAddress, A); +} +break; + +case 0x81: +{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); +} +{ + poke(operandAddress, A); +} +break; + +case 0x91: +{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operandAddress += Y; +} +{ + poke(operandAddress, A); +} +break; + + +case 0x86: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, X); +} +break; + +case 0x96: +{ + operandAddress = (uInt8)(peek(PC++) + Y); +} +{ + poke(operandAddress, X); +} +break; + +case 0x8e: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; +} +{ + poke(operandAddress, X); +} +break; + + +case 0x84: +{ + operandAddress = peek(PC++); +} +{ + poke(operandAddress, Y); +} +break; + +case 0x94: +{ + operandAddress = (uInt8)(peek(PC++) + X); +} +{ + poke(operandAddress, Y); +} +break; + +case 0x8c: +{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; +} +{ + poke(operandAddress, Y); +} +break; + + +case 0xaa: +{ +} +{ + X = A; + notZ = X; + N = X & 0x80; +} +break; + + +case 0xa8: +{ +} +{ + Y = A; + notZ = Y; + N = Y & 0x80; +} +break; + + +case 0xba: +{ +} +{ + X = SP; + notZ = X; + N = X & 0x80; +} +break; + + +case 0x8a: +{ +} +{ + A = X; + notZ = A; + N = A & 0x80; +} +break; + + +case 0x9a: +{ +} +{ + SP = X; +} +break; + + +case 0x98: +{ +} +{ + A = Y; + notZ = A; + N = A & 0x80; +} +break; + + diff --git a/stella/src/build/makefile b/stella/src/build/makefile new file mode 100644 index 000000000..9f95a0961 --- /dev/null +++ b/stella/src/build/makefile @@ -0,0 +1,319 @@ +##============================================================================ +## +## SSSS tt lll lll +## SS SS tt ll ll +## SS tttttt eeee ll ll aaaa +## SSSS tt ee ee ll ll aa +## SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +## SS SS tt ee ll ll aa aa +## SSSS ttt eeeee llll llll aaaaa +## +## Copyright (c) 1995-1999 by Bradford W. Mott +## +## See the file "license" for information on usage and redistribution of +## this file, and for a DISCLAIMER OF ALL WARRANTIES. +## +## $Id: makefile,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +##============================================================================ + +##============================================================================ +## +## The following options are used during compiling. You may need to +## define some of these on the CXXFLAGS line if you're running on an +## "unsupported" system +## +## -DBSPF_BOOL if your C++ compiler doesn't support the bool type +## +## -DSHOW_TIMING if your want some timing information displayed when +## you exit the program +## +## -DDEBUG if you want a 6507 trace written to stdout +## +## -DLINUX_JOYSTICK to include linux joystick driver support +## (requires you to install the joystick kernel module) +## +##============================================================================ + +CXX = g++ +LD = g++ + +LDFLAGS = +LDLIBS = + +SRC = .. +CORE = $(SRC)/emucore +UI = $(SRC)/ui + +INCLUDES = -I. -I$(CORE) -I$(CORE)/m6502/src -I$(CORE)/m6502/src/bspf/src + +CXXFLAGS = -O -Wall $(INCLUDES) $(SYS_INCLUDES) + +default: + @echo "" + @echo "To build Stella type: 'make '" + @echo "" + @echo "where is one of:" + @echo "" + @echo " dos DOS version using DJGPP" + @echo " unix-x Generic Unix X windows version" + @echo " linux-x Linux X windows version" + @echo " linux-x-joy Linux X windows version with joystick" + @echo " bsdi-x BSD/OS 4.0 X Windows version" + @echo " solaris-x Solaris X windows version" + @echo "" + @echo "Hopefully new versions will be added soon!" + @echo "" + +dos: + make stella.exe \ + LD="gxx" \ + CXX="gcc" \ + INCLUDES="$(INCLUDES) -I$(UI)/dos -I$(UI)/sound" \ + OPTIONS="-DBSPF_DOS" \ + LDFLAGS="" \ + LDLIBS="" \ + OBJS="mainDOS.o PCJoys.o SndDOS.o sbdrv.o TIASound.o" + +unix-x: + make xstella \ + INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \ + SYS_INCLUDES="" \ + OPTIONS="-DBSPF_UNIX" \ + LDFLAGS="-L/usr/X11R6/lib" \ + LDLIBS="-lX11 -lXext" \ + OBJS="mainX11.o SndUnix.o" + +linux-x: + make xstella \ + INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \ + SYS_INCLUDES="" \ + OPTIONS="-DBSPF_UNIX" \ + LDFLAGS="-L/usr/X11R6/lib" \ + LDLIBS="-lX11 -lXext" \ + OBJS="mainX11.o SndUnix.o" + +linux-x-joy: + make xstella \ + INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \ + SYS_INCLUDES="" \ + OPTIONS="-DBSPF_UNIX -DLINUX_JOYSTICK" \ + LDFLAGS="-L/usr/X11R6/lib" \ + LDLIBS="-lX11 -lXext" \ + OBJS="mainX11.o SndUnix.o" + +bsdi-x: + make xstella \ + INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \ + SYS_INCLUDES="-I/usr/X11R6/include" \ + OPTIONS="-DBSPF_UNIX" \ + LDFLAGS="-L/usr/X11R6/lib" \ + LDLIBS="-lX11 -lXext" \ + OBJS="mainX11.o SndUnix.o" + +solaris-x: + make xstella \ + INCLUDES="$(INCLUDES) -I$(UI)/x11 -I$(UI)/sound" \ + SYS_INCLUDES="-I/usr/openwin/include" \ + OPTIONS="-DBSPF_UNIX" \ + LDFLAGS="-L/usr/openwin/lib" \ + LDLIBS="-lX11 -lXext" \ + OBJS="mainX11.o SndUnix.o" + +############################################################################### +## List of "core" object files +############################################################################### +M6502_OBJS = D6502.o Device.o M6502.o M6502Low.o M6502Hi.o NullDev.o System.o + +CORE_OBJS = Booster.o Cart.o Cart2K.o Cart3F.o Cart4K.o CartAR.o CartE0.o \ + CartE7.o CartF4SC.o CartF6.o CartF6SC.o CartF8.o CartF8SC.o \ + CartFASC.o CartFE.o CartMC.o Console.o \ + Control.o DefProps.o Driving.o \ + Event.o Joystick.o Keyboard.o M6532.o MD5.o MediaSrc.o Paddles.o \ + Props.o PropsSet.o Random.o Sound.o Switches.o TIA.o \ + $(M6502_OBJS) + +stella.exe: $(CORE_OBJS) $(OBJS) + $(LD) -o a.exe *.o $(LDFLAGS) $(LDLIBS) + exe2coff a.exe + strip a + copy /B \djgpp\bin\pmodstub.exe + a stella.exe + del a + del a.exe + +xstella: $(CORE_OBJS) $(OBJS) + $(LD) -o xstella $(CORE_OBJS) $(OBJS) $(LDFLAGS) $(LDLIBS) + +M6502Low.ins: $(CORE)/m6502/src/M6502Low.m4 $(CORE)/m6502/src/M6502.m4 + m4 $(CORE)/m6502/src/M6502Low.m4 $(CORE)/m6502/src/M6502.m4 > M6502Low.ins + +M6502Hi.ins: $(CORE)/m6502/src/M6502Hi.m4 $(CORE)/m6502/src/M6502.m4 + m4 $(CORE)/m6502/src/M6502Hi.m4 $(CORE)/m6502/src/M6502.m4 > M6502Hi.ins + +DefProps.def: $(CORE)/stella.pro + sed 's/"/\\\"/g;s/^/"/g;s/$$/",/g' $(CORE)/stella.pro > DefProps.def + +DefProps.o: DefProps.def +M6502Low.o: M6502Low.ins +M6502Hi.o: M6502Hi.ins + +cleandos: + del *.o + del stella.exe + del DefProps.def + del M6502Low.ins + del M6502Hi.ins + +clean: + rm -f *.o stella xstella stella.exe core + +cleanall: clean + rm -f DefProps.def M6502Low.ins M6502Hi.ins + +Driving.o: $(CORE)/Driving.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Driving.cxx + +Event.o: $(CORE)/Event.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Event.cxx + +Control.o: $(CORE)/Control.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Control.cxx + +Joystick.o: $(CORE)/Joystick.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Joystick.cxx + +Keyboard.o: $(CORE)/Keyboard.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Keyboard.cxx + +Paddles.o: $(CORE)/Paddles.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Paddles.cxx + +Booster.o: $(CORE)/Booster.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Booster.cxx + +Cart.o: $(CORE)/Cart.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Cart.cxx + +Cart2K.o: $(CORE)/Cart2K.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Cart2K.cxx + +Cart3F.o: $(CORE)/Cart3F.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Cart3F.cxx + +Cart4K.o: $(CORE)/Cart4K.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Cart4K.cxx + +CartAR.o: $(CORE)/CartAR.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartAR.cxx + +CartE0.o: $(CORE)/CartE0.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartE0.cxx + +CartE7.o: $(CORE)/CartE7.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartE7.cxx + +CartF4SC.o: $(CORE)/CartF4SC.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartF4SC.cxx + +CartF6.o: $(CORE)/CartF6.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartF6.cxx + +CartF6SC.o: $(CORE)/CartF6SC.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartF6SC.cxx + +CartF8.o: $(CORE)/CartF8.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartF8.cxx + +CartF8SC.o: $(CORE)/CartF8SC.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartF8SC.cxx + +CartFASC.o: $(CORE)/CartFASC.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartFASC.cxx + +CartFE.o: $(CORE)/CartFE.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartFE.cxx + +CartMC.o: $(CORE)/CartMC.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/CartMC.cxx + +DefProps.o: $(CORE)/DefProps.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/DefProps.cxx + +M6532.o: $(CORE)/M6532.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/M6532.cxx + +TIA.o: $(CORE)/TIA.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/TIA.cxx + +Console.o: $(CORE)/Console.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Console.cxx + +MD5.o: $(CORE)/MD5.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/MD5.cxx + +MediaSrc.o: $(CORE)/MediaSrc.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/MediaSrc.cxx + +PropsSet.o: $(CORE)/PropsSet.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/PropsSet.cxx + +Props.o: $(CORE)/Props.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Props.cxx + +Random.o: $(CORE)/Random.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Random.cxx + +Switches.o: $(CORE)/Switches.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Switches.cxx + +Sound.o: $(CORE)/Sound.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/Sound.cxx + + +Terminal.o: $(UI)/x11/Terminal.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/x11/Terminal.cxx + +mainDOS.o: $(UI)/dos/mainDOS.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/mainDOS.cxx + +PCJoys.o: $(UI)/dos/PCJoys.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/PCJoys.cxx + +SndDOS.o: $(UI)/dos/SndDOS.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/SndDOS.cxx + +sbdrv.o: $(UI)/dos/sbdrv.c + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/dos/sbdrv.c + +TIASound.o: $(UI)/sound/TIASound.c + $(CXX) -c -DWIN32 $(CXXFLAGS) $(OPTIONS) $(UI)/sound/TIASound.c + +TermX11.o: $(UI)/x11/TermX11.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/x11/TermX11.cxx + +mainX11.o: $(UI)/x11/mainX11.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/x11/mainX11.cxx + +SndUnix.o: $(UI)/sound/SndUnix.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(UI)/sound/SndUnix.cxx + +D6502.o: $(CORE)/m6502/src/D6502.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/D6502.cxx + +Device.o: $(CORE)/m6502/src/Device.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/Device.cxx + +M6502.o: $(CORE)/m6502/src/M6502.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/M6502.cxx + +M6502Low.o: $(CORE)/m6502/src/M6502Low.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/M6502Low.cxx + +M6502Hi.o: $(CORE)/m6502/src/M6502Hi.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/M6502Hi.cxx + +NullDev.o: $(CORE)/m6502/src/NullDev.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/NullDev.cxx + +System.o: $(CORE)/m6502/src/System.cxx + $(CXX) -c $(CXXFLAGS) $(OPTIONS) $(CORE)/m6502/src/System.cxx + diff --git a/stella/src/emucore/Booster.cxx b/stella/src/emucore/Booster.cxx new file mode 100644 index 000000000..d099bf74e --- /dev/null +++ b/stella/src/emucore/Booster.cxx @@ -0,0 +1,105 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Booster.cxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#include "Event.hxx" +#include "Booster.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +BoosterGrip::BoosterGrip(Jack jack, const Event& event) + : Controller(jack, event) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +BoosterGrip::~BoosterGrip() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool BoosterGrip::read(DigitalPin pin) +{ + switch(pin) + { + case One: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroUp) == 0) : + (myEvent.get(Event::JoystickOneUp) == 0); + + case Two: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroDown) == 0) : + (myEvent.get(Event::JoystickOneDown) == 0); + + case Three: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroLeft) == 0) : + (myEvent.get(Event::JoystickOneLeft) == 0); + + case Four: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroRight) == 0) : + (myEvent.get(Event::JoystickOneRight) == 0); + + case Six: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroFire) == 0) : + (myEvent.get(Event::JoystickOneFire) == 0); + + default: + return true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 BoosterGrip::read(AnalogPin pin) +{ + // The CBS Booster-grip has two more buttons on it. These buttons are + // connected to the inputs usually used by paddles. + + switch(pin) + { + case Five: + if(myJack == Left) + { + return (myEvent.get(Event::BoosterGripZeroBooster) != 0) ? + minimumResistance : maximumResistance; + } + else + { + return (myEvent.get(Event::BoosterGripOneBooster) != 0) ? + minimumResistance : maximumResistance; + } + + case Nine: + if(myJack == Left) + { + return (myEvent.get(Event::BoosterGripZeroTrigger) != 0) ? + minimumResistance : maximumResistance; + } + else + { + return (myEvent.get(Event::BoosterGripOneTrigger) != 0) ? + minimumResistance : maximumResistance; + } + + default: + return maximumResistance; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void BoosterGrip::write(DigitalPin, bool) +{ + // Writing doesn't do anything to the booster grip... +} + diff --git a/stella/src/emucore/Booster.hxx b/stella/src/emucore/Booster.hxx new file mode 100644 index 000000000..7549456a6 --- /dev/null +++ b/stella/src/emucore/Booster.hxx @@ -0,0 +1,78 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Booster.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#ifndef BOOSTERGRIP_HXX +#define BOOSTERGRIP_HXX + +#include "bspf.hxx" +#include "Control.hxx" + +/** + The standard Atari 2600 joystick controller fitted with the + CBS Booster grip. The Booster grip has two more fire buttons + on it (a booster and a trigger). + + @author Bradford W. Mott + @version $Id: Booster.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +*/ +class BoosterGrip : public Controller +{ + public: + /** + Create a new booster grip joystick plugged into the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + */ + BoosterGrip(Jack jack, const Event& event); + + /** + Destructor + */ + virtual ~BoosterGrip(); + + public: + /** + Read the value of the specified digital pin for this controller. + + @param pin The pin of the controller jack to read + @return The state of the pin + */ + virtual bool read(DigitalPin pin); + + /** + Read the resistance at the specified analog pin for this controller. + The returned value is the resistance measured in ohms. + + @param pin The pin of the controller jack to read + @return The resistance at the specified pin + */ + virtual Int32 read(AnalogPin pin); + + /** + Write the given value to the specified digital pin for this + controller. Writing is only allowed to the pins associated + with the PIA. Therefore you cannot write to pin six. + + @param pin The pin of the controller jack to write to + @param value The value to write to the pin + */ + virtual void write(DigitalPin pin, bool value); +}; +#endif + diff --git a/stella/src/emucore/Cart.cxx b/stella/src/emucore/Cart.cxx new file mode 100644 index 000000000..26bd51144 --- /dev/null +++ b/stella/src/emucore/Cart.cxx @@ -0,0 +1,215 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart.cxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#include +#include +#include "Cart.hxx" +#include "Cart2K.hxx" +#include "Cart3F.hxx" +#include "Cart4K.hxx" +#include "CartAR.hxx" +#include "CartE0.hxx" +#include "CartE7.hxx" +#include "CartF4SC.hxx" +#include "CartF6.hxx" +#include "CartF6SC.hxx" +#include "CartF8.hxx" +#include "CartF8SC.hxx" +#include "CartFASC.hxx" +#include "CartFE.hxx" +#include "CartMC.hxx" +#include "MD5.hxx" +#include "Props.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge* Cartridge::create(const uInt8* image, uInt32 size, + const Properties& properties) +{ + Cartridge* cartridge = 0; + + // Get the type of the cartridge we're creating + string type = properties.get("Cartridge.Type"); + + // See if we should try to auto-detect the cartridge type + if(type == "Auto-detect") + { + type = autodetectType(image, size); + } + + // We should know the cart's type by now so let's create it + if(type == "2K") + cartridge = new Cartridge2K(image); + else if(type == "3F") + cartridge = new Cartridge3F(image, size); + else if(type == "4K") + cartridge = new Cartridge4K(image); + else if(type == "AR") + cartridge = new CartridgeAR(image, size); + else if(type == "E0") + cartridge = new CartridgeE0(image); + else if(type == "E7") + cartridge = new CartridgeE7(image); + else if(type == "F4SC") + cartridge = new CartridgeF4SC(image); + else if(type == "F6") + cartridge = new CartridgeF6(image); + else if(type == "F6SC") + cartridge = new CartridgeF6SC(image); + else if(type == "F8") + cartridge = new CartridgeF8(image); + else if(type == "F8SC") + cartridge = new CartridgeF8SC(image); + else if(type == "FASC") + cartridge = new CartridgeFASC(image); + else if(type == "FE") + cartridge = new CartridgeFE(image); + else if(type == "MC") + cartridge = new CartridgeMC(image, size); + else + { + // TODO: At some point this should be handled in a better way... + assert(false); + } + + return cartridge; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge::Cartridge() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge::~Cartridge() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Cartridge::autodetectType(const uInt8* image, uInt32 size) +{ + // The following is a simple table mapping games to type's using MD5 values + struct MD5ToType + { + const char* md5; + const char* type; + }; + + static MD5ToType table[] = { + {"5336f86f6b982cc925532f2e80aa1e17", "E0"}, // Death Star + {"b311ab95e85bc0162308390728a7361d", "E0"}, // Gyruss + {"c29f8db680990cb45ef7fef6ab57a2c2", "E0"}, // Super Cobra + {"085322bae40d904f53bdcc56df0593fc", "E0"}, // Tutankamn + {"c7f13ef38f61ee2367ada94fdcc6d206", "E0"}, // Popeye + {"6339d28c9a7f92054e70029eb0375837", "E0"}, // Star Wars, Arcade + {"27c6a2ca16ad7d814626ceea62fa8fb4", "E0"}, // Frogger II + {"3347a6dd59049b15a38394aa2dafa585", "E0"}, // Montezuma's Revenge + {"6dda84fb8e442ecf34241ac0d1d91d69", "F6SC"}, // Dig Dug + {"57fa2d09c9e361de7bd2aa3a9575a760", "F8SC"}, // Stargate + {"3a771876e4b61d42e3a3892ad885d889", "F8SC"}, // Defender ][ + {"efefc02bbc5258815457f7a5b8d8750a", "FASC"}, // Tunnel runner + {"7e51a58de2c0db7d33715f518893b0db", "FASC"}, // Mountain King + {"9947f1ebabb56fd075a96c6d37351efa", "FASC"}, // Omega Race + {"0443cfa9872cdb49069186413275fa21", "E7"}, // Burger Timer + {"76f53abbbf39a0063f24036d6ee0968a", "E7"}, // Bump-N-Jump + {"3b76242691730b2dd22ec0ceab351bc6", "E7"}, // He-Man + {"ac7c2260378975614192ca2bc3d20e0b", "FE"}, // Decathlon + {"4f618c2429138e0280969193ed6c107e", "FE"}, // Robot Tank + {(char*)0, (char*)0} + }; + + // Get the MD5 message-digest for the ROM image + string md5 = MD5(image, size); + + // Take a closer look at the ROM image and try to figure out its type + const char* type = 0; + + // First we'll see if it's type is listed in the table above + for(MD5ToType* entry = table; (entry->md5 != 0); ++entry) + { + if(entry->md5 == md5) + { + type = entry->type; + break; + } + } + + // If we didn't find the type in the table then guess it based on size + if(type == 0) + { + if((size % 8448) == 0) + { + type = "AR"; + } + else if((size == 2048) || (memcmp(image, image + 2048, 2048) == 0)) + { + type = "2K"; + } + else if((size == 4096) || (memcmp(image, image + 4096, 4096) == 0)) + { + type = "4K"; + } + else if((size == 8192) || (memcmp(image, image + 8192, 8192) == 0)) + { + type = "F8"; + } + else if(size == 12288) + { + type = "FASC"; + } + else if(size == 32768) + { + type = "F4SC"; + } + else if(size == 131072) + { + type = "MC"; + } + else + { + // Assume this is a 16K super-cart then check to see if it is + type = "F6SC"; + + uInt8 first = image[0]; + for(uInt32 i = 0; i < 256; ++i) + { + if(image[i] != first) + { + // It's not a super cart (probably) + type = "F6"; + break; + } + } + } + } + + return type; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge::Cartridge(const Cartridge&) +{ + assert(false); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge& Cartridge::operator = (const Cartridge&) +{ + assert(false); + return *this; +} + diff --git a/stella/src/emucore/Cart.hxx b/stella/src/emucore/Cart.hxx new file mode 100644 index 000000000..36d00bd47 --- /dev/null +++ b/stella/src/emucore/Cart.hxx @@ -0,0 +1,80 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGE_HXX +#define CARTRIDGE_HXX + +class Cartridge; +class Properties; +class System; + +#include "bspf.hxx" +#include "Device.hxx" + +/** + A cartridge is a device which contains the machine code for a + game and handles any bankswitching performed by the cartridge. + + @author Bradford W. Mott + @version $Id: Cart.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +*/ +class Cartridge : public Device +{ + public: + /** + Create a new cartridge object allocated on the heap. The + type of cartridge created depends on the properties object. + + @param image A pointer to the ROM image + @param size The size of the ROM image + @param properties The properties associated with the game + @return Pointer to the new cartridge object allocated on the heap + */ + static Cartridge* create(const uInt8* image, uInt32 size, + const Properties& properties); + + public: + /** + Create a new cartridge + */ + Cartridge(); + + /** + Destructor + */ + virtual ~Cartridge(); + + private: + /** + Try to auto-detect the bankswitching type of the cartridge + + @param image A pointer to the ROM image + @param size The size of the ROM image + @return The "best guess" for the cartridge type + */ + static string autodetectType(const uInt8* image, uInt32 size); + + private: + // Copy constructor isn't supported by cartridges so make it private + Cartridge(const Cartridge&); + + // Assignment operator isn't supported by cartridges so make it private + Cartridge& operator = (const Cartridge&); +}; +#endif + diff --git a/stella/src/emucore/Cart2K.cxx b/stella/src/emucore/Cart2K.cxx new file mode 100644 index 000000000..8a0fc3f2e --- /dev/null +++ b/stella/src/emucore/Cart2K.cxx @@ -0,0 +1,82 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart2K.cxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#include +#include "Cart2K.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge2K::Cartridge2K(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 2048; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge2K::~Cartridge2K() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* Cartridge2K::name() const +{ + return "Cartridge2K"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge2K::reset() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge2K::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1000 & mask) == 0); + + System::PageAccess access; + access.directPokeBase = 0; + access.device = this; + + // Map ROM image into the system + for(uInt32 address = 0x1000; address < 0x2000; address += (1 << shift)) + { + access.directPeekBase = &myImage[address & 0x07FF]; + mySystem->setPageAccess(address >> mySystem->pageShift(), access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 Cartridge2K::peek(uInt16 address) +{ + return myImage[address & 0x07FF]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge2K::poke(uInt16, uInt8) +{ + // This is ROM so poking has no effect :-) +} + diff --git a/stella/src/emucore/Cart2K.hxx b/stella/src/emucore/Cart2K.hxx new file mode 100644 index 000000000..601c16b1c --- /dev/null +++ b/stella/src/emucore/Cart2K.hxx @@ -0,0 +1,93 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart2K.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGE2K_HXX +#define CARTRIDGE2K_HXX + +class Cartridge2K; +class System; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the standard Atari 2K cartridge. These cartridges + are not bankswitched, however, the data repeats twice in the + 2600's 4K cartridge addressing space. + + @author Bradford W. Mott + @version $Id: Cart2K.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +*/ +class Cartridge2K : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + Cartridge2K(const uInt8* image); + + /** + Destructor + */ + virtual ~Cartridge2K(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset cartridge to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + // The 2k ROM image for the cartridge + uInt8 myImage[2048]; +}; +#endif + diff --git a/stella/src/emucore/Cart3F.cxx b/stella/src/emucore/Cart3F.cxx new file mode 100644 index 000000000..a7bdd27d7 --- /dev/null +++ b/stella/src/emucore/Cart3F.cxx @@ -0,0 +1,148 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart3F.cxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#include +#include "Cart3F.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge3F::Cartridge3F(const uInt8* image, uInt32 size) + : mySize(size) +{ + // Allocate array for the ROM image + myImage = new uInt8[mySize]; + + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < mySize; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge3F::~Cartridge3F() +{ + delete[] myImage; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* Cartridge3F::name() const +{ + return "Cartridge3F"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3F::reset() +{ + // We'll map bank 0 into the first segment upon reset + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3F::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1800 & mask) == 0); + + // Set the page accessing methods for the hot spots (for 100% emulation + // I would need to chain any accesses below 0x40 to the TIA but for + // now I'll just forget about them) + System::PageAccess access; + for(uInt32 i = 0x00; i < 0x40; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Setup the second segment to always point to the last ROM slice + for(uInt32 j = 0x1800; j < 0x2000; j += (1 << shift)) + { + access.device = this; + access.directPeekBase = &myImage[(mySize - 2048) + (j & 0x07FF)]; + access.directPokeBase = 0; + mySystem->setPageAccess(j >> shift, access); + } + + // Install pages for bank 0 into the first segment + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 Cartridge3F::peek(uInt16 address) +{ + address = address & 0x0FFF; + + if(address < 0x0800) + { + return myImage[(address & 0x07FF) + myCurrentBank * 2048]; + } + else + { + return myImage[(address & 0x07FF) + mySize - 2048]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3F::poke(uInt16 address, uInt8 value) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + if(address <= 0x003F) + { + bank(value); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge3F::bank(uInt16 bank) +{ + // Make sure the bank they're asking for is reasonable + if((uInt32)bank * 2048 < mySize) + { + myCurrentBank = bank; + } + else + { + // Oops, the bank they're asking for isn't valid so let's wrap it + // around to a valid bank number + myCurrentBank = bank % (mySize / 2048); + } + + uInt32 offset = myCurrentBank * 2048; + uInt16 shift = mySystem->pageShift(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1000; address < 0x1800; address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x07FF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/Cart3F.hxx b/stella/src/emucore/Cart3F.hxx new file mode 100644 index 000000000..52f90a511 --- /dev/null +++ b/stella/src/emucore/Cart3F.hxx @@ -0,0 +1,112 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart3F.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGE3F_HXX +#define CARTRIDGE3F_HXX + +class Cartridge3F; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the cartridge class for Tigervision's bankswitched + games. In this bankswitching scheme the 2600's 4K cartridge + address space is broken into two 2K segments. The last 2K + segment always points to the last 2K of the ROM image. The + desired bank number of the first 2K segment is selected by + storing its value into $3F. Actually, any write to location + $00 to $3F will change banks. Although, the Tigervision games + only used 8K this bankswitching scheme supports up to 512K. + + @author Bradford W. Mott + @version $Id: Cart3F.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +*/ +class Cartridge3F : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image and size + + @param image Pointer to the ROM image + @param size The size of the ROM image + */ + Cartridge3F(const uInt8* image, uInt32 size); + + /** + Destructor + */ + virtual ~Cartridge3F(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Map the specified bank into the first segment + + @param bank The bank that should be mapped + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active for the first segment + uInt16 myCurrentBank; + + // Pointer to a dynamically allocated ROM image of the cartridge + uInt8* myImage; + + // Size of the ROM image + uInt32 mySize; +}; +#endif + diff --git a/stella/src/emucore/Cart4K.cxx b/stella/src/emucore/Cart4K.cxx new file mode 100644 index 000000000..f5727b431 --- /dev/null +++ b/stella/src/emucore/Cart4K.cxx @@ -0,0 +1,82 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart4K.cxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#include +#include "Cart4K.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge4K::Cartridge4K(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 4096; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Cartridge4K::~Cartridge4K() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* Cartridge4K::name() const +{ + return "Cartridge4K"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge4K::reset() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge4K::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1000 & mask) == 0); + + System::PageAccess access; + access.directPokeBase = 0; + access.device = this; + + // Map ROM image into the system + for(uInt32 address = 0x1000; address < 0x2000; address += (1 << shift)) + { + access.directPeekBase = &myImage[address & 0x0FFF]; + mySystem->setPageAccess(address >> mySystem->pageShift(), access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 Cartridge4K::peek(uInt16 address) +{ + return myImage[address & 0x0FFF]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Cartridge4K::poke(uInt16, uInt8) +{ + // This is ROM so poking has no effect :-) +} + diff --git a/stella/src/emucore/Cart4K.hxx b/stella/src/emucore/Cart4K.hxx new file mode 100644 index 000000000..f7d6bef07 --- /dev/null +++ b/stella/src/emucore/Cart4K.hxx @@ -0,0 +1,92 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Cart4K.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGE4K_HXX +#define CARTRIDGE4K_HXX + +class Cartridge4K; +class System; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the standard Atari 4K cartridge. These cartridges are + not bankswitched. + + @author Bradford W. Mott + @version $Id: Cart4K.hxx,v 1.1.1.1 2001-12-27 19:54:18 bwmott Exp $ +*/ +class Cartridge4K : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + Cartridge4K(const uInt8* image); + + /** + Destructor + */ + virtual ~Cartridge4K(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset cartridge to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + // The 4K ROM image for the cartridge + uInt8 myImage[4096]; +}; +#endif + diff --git a/stella/src/emucore/CartAR.cxx b/stella/src/emucore/CartAR.cxx new file mode 100644 index 000000000..b7c845bc5 --- /dev/null +++ b/stella/src/emucore/CartAR.cxx @@ -0,0 +1,420 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartAR.cxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#include +#include +#include "CartAR.hxx" +#include "M6502Hi.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeAR::CartridgeAR(const uInt8* image, uInt32 size) + : my6502(0) +{ + uInt32 i; + + // Create a load image buffer and copy the given image + myLoadImages = new uInt8[size]; + myNumberOfLoadImages = size / 8448; + memcpy(myLoadImages, image, size); + + // Initialize RAM with random values + Random random; + for(i = 0; i < 6 * 1024; ++i) + { + myImage[i] = random.next(); + } + + // Initialize ROM with an invalid 6502 opcode + for(i = 6 * 1024; i < 8 * 1024; ++i) + { + myImage[i] = 0xFF; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeAR::~CartridgeAR() +{ + delete[] myLoadImages; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeAR::name() const +{ + return "CartridgeAR"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::reset() +{ + // Try to load the first load into RAM upon reset + loadIntoRAM(0); + + myPower = true; + myPowerRomCycle = 0; + myWriteEnabled = false; + + myLastAccess = 0; + myNumberOfDistinctAccesses = 0; + myWritePending = false; + + // Set bank configuration upon reset so ROM is selected + myImageOffset[0] = 0 * 2048; + myImageOffset[1] = 3 * 2048; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::systemCyclesReset() +{ + // Get the current system cycle + uInt32 cycles = mySystem->cycles(); + + // Adjust cycle values + myPowerRomCycle -= cycles; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + my6502 = &(M6502High&)mySystem->m6502(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1000 & mask) == 0); + + System::PageAccess access; + for(uInt32 i = 0x1000; i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + bankConfiguration(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeAR::peek(uInt16 addr) +{ + // Check to see if the Supercharger ROM is being accessed? + if(myImageOffset[1] == 3 * 2048) + { + Int32 cycles = mySystem->cycles(); + + // Is the tape rewind routine being accessed? + if((addr & 0x1FFF) == 0x180A) + { + // See if the ROM has been powered up long enough + if(!myPower || (myPower && ((myPowerRomCycle + 7) > cycles))) + { + cerr << "ERROR: Supercharger ROM has not been powered up!\n"; + } + else + { + cerr << "ERROR: Supercharger code doesn't handle rewinding tape!\n"; + } + } + // Is the multiload routine being accessed? + else if((addr & 0x1FFF) == 0x1800) + { + // See if the ROM has been powered up long enough + if(!myPower || (myPower && ((myPowerRomCycle + 7) > cycles))) + { + cerr << "ERROR: Supercharger ROM has not been powered up!\n"; + } + else + { + // Get the load they're trying to access + uInt8 load = mySystem->peek(0x00FA); + + // Load the specified load into RAM + loadIntoRAM(load); + + return myImage[(addr & 0x07FF) + myImageOffset[1]]; + } + } + } + + // Are the "value" registers being accessed? + if(!(addr & 0x0F00) && (!myWriteEnabled || !myWritePending)) + { + myLastAccess = addr; + myNumberOfDistinctAccesses = my6502->distinctAccesses(); + myWritePending = true; + } + // Is the bank configuration hotspot being accessed? + else if((addr & 0x1FFF) == 0x1FF8) + { + // Yes, so handle bank configuration + myWritePending = false; + bankConfiguration(myLastAccess); + } + // Handle poke if writing enabled + else if(myWriteEnabled && myWritePending) + { + if(my6502->distinctAccesses() >= myNumberOfDistinctAccesses + 5) + { + if(my6502->distinctAccesses() == myNumberOfDistinctAccesses + 5) + { + if((addr & 0x0800) == 0) + myImage[(addr & 0x07FF) + myImageOffset[0]] = myLastAccess; + else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-) + myImage[(addr & 0x07FF) + myImageOffset[1]] = myLastAccess; + } + myWritePending = false; + } + } + + return myImage[(addr & 0x07FF) + myImageOffset[(addr & 0x0800) ? 1 : 0]]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::poke(uInt16 addr, uInt8) +{ + // Are the "value" registers being accessed? + if(!(addr & 0x0F00) && (!myWriteEnabled || !myWritePending)) + { + myLastAccess = addr; + myNumberOfDistinctAccesses = my6502->distinctAccesses(); + myWritePending = true; + } + // Is the bank configuration hotspot being accessed? + else if((addr & 0x1FFF) == 0x1FF8) + { + // Yes, so handle bank configuration + myWritePending = false; + bankConfiguration(myLastAccess); + } + // Handle poke if writing enabled + else if(myWriteEnabled && myWritePending) + { + if(my6502->distinctAccesses() >= myNumberOfDistinctAccesses + 5) + { + if(my6502->distinctAccesses() == myNumberOfDistinctAccesses + 5) + { + if((addr & 0x0800) == 0) + myImage[(addr & 0x07FF) + myImageOffset[0]] = myLastAccess; + else if(myImageOffset[1] != 3 * 2048) // Can't poke to ROM :-) + myImage[(addr & 0x07FF) + myImageOffset[1]] = myLastAccess; + } + myWritePending = false; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::bankConfiguration(uInt8 configuration) +{ + // D7-D5 of this byte: Write Pulse Delay (n/a for emulator) + // + // D4-D0: RAM/ROM configuration: + // $F000-F7FF $F800-FFFF Address range that banks map into + // 000wp 2 ROM + // 001wp 0 ROM + // 010wp 2 0 as used in Commie Mutants and many others + // 011wp 0 2 as used in Suicide Mission + // 100wp 2 ROM + // 101wp 1 ROM + // 110wp 2 1 as used in Killer Satellites + // 111wp 1 2 as we use for 2k/4k ROM cloning + // + // w = Write Enable (1 = enabled; accesses to $F000-$F0FF cause writes + // to happen. 0 = disabled, and the cart acts like ROM.) + // p = ROM Power (0 = enabled, 1 = off.) Only power the ROM if you're + // wanting to access the ROM for multiloads. Otherwise set to 1. + + // Handle ROM power configuration + myPower = !(configuration & 0x01); + + if(myPower) + { + myPowerRomCycle = mySystem->cycles(); + } + + myWriteEnabled = configuration & 0x02; + + switch((configuration >> 2) & 0x07) + { + case 0: + { + myImageOffset[0] = 2 * 2048; + myImageOffset[1] = 3 * 2048; + break; + } + + case 1: + { + myImageOffset[0] = 0 * 2048; + myImageOffset[1] = 3 * 2048; + break; + } + + case 2: + { + myImageOffset[0] = 2 * 2048; + myImageOffset[1] = 0 * 2048; + break; + } + + case 3: + { + myImageOffset[0] = 0 * 2048; + myImageOffset[1] = 2 * 2048; + break; + } + + case 4: + { + myImageOffset[0] = 2 * 2048; + myImageOffset[1] = 3 * 2048; + break; + } + + case 5: + { + myImageOffset[0] = 1 * 2048; + myImageOffset[1] = 3 * 2048; + break; + } + + case 6: + { + myImageOffset[0] = 2 * 2048; + myImageOffset[1] = 1 * 2048; + break; + } + + case 7: + { + myImageOffset[0] = 1 * 2048; + myImageOffset[1] = 2 * 2048; + break; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::setupROM() +{ + static uInt8 dummyROMCode[] = { + 0xa9, 0x0, 0xa2, 0x0, 0x95, 0x80, 0xe8, 0xe0, + 0x80, 0xd0, 0xf9, 0x4c, 0x2b, 0xfa, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xa9, 0x0, 0xa2, 0x0, 0x95, 0x80, 0xe8, 0xe0, + 0x1e, 0xd0, 0xf9, 0xa2, 0x0, 0xbd, 0x45, 0xfa, + 0x95, 0xfa, 0xe8, 0xe0, 0x6, 0xd0, 0xf6, 0xa2, + 0xff, 0xa0, 0x0, 0xa9, 0x0, 0x85, 0x80, 0xcd, + 0x0, 0xf0, 0x4c, 0xfa, 0x0, 0xad, 0xf8, 0xff, + 0x4c, 0x0, 0x0 + }; + + int size = sizeof(dummyROMCode); + + // Copy the "dummy" ROM code into the ROM area + for(int i = 0; i < size; ++i) + { + myImage[0x1A00 + i] = dummyROMCode[i]; + } + + // Put a JMP $FA20 at multiload entry point ($F800) + myImage[0x1800] = 0x4C; + myImage[0x1801] = 0x20; + myImage[0x1802] = 0xFA; + + // Update ROM code to have the correct reset address and bank configuration + myImage[0x1A00 + size - 2] = myHeader[0]; + myImage[0x1A00 + size - 1] = myHeader[1]; + myImage[0x1A00 + size - 11] = myHeader[2]; + myImage[0x1A00 + size - 15] = myHeader[2]; + + // Finally set 6502 vectors to point to this "dummy" code at 0xFA00 + myImage[3 * 2048 + 2044] = 0x00; + myImage[3 * 2048 + 2045] = 0xFA; + myImage[3 * 2048 + 2046] = 0x00; + myImage[3 * 2048 + 2047] = 0xFA; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeAR::checksum(uInt8* s, uInt16 length) +{ + uInt8 sum = 0; + + for(uInt32 i = 0; i < length; ++i) + { + sum += s[i]; + } + + return sum; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeAR::loadIntoRAM(uInt8 load) +{ + uInt16 image; + + // Scan through all of the loads to see if we find the one we're looking for + for(image = 0; image < myNumberOfLoadImages; ++image) + { + // Is this the correct load? + if(myLoadImages[(image * 8448) + 8192 + 5] == load) + { + // Copy the load's header + memcpy(myHeader, myLoadImages + (image * 8448) + 8192, 256); + + // Verify the load's header + if(checksum(myHeader, 8) != 0x55) + { + cerr << "WARNING: The Supercharger header checksum is invalid...\n"; + } + + // Load all of the pages from the load + bool invalidPageChecksumSeen = false; + for(uInt32 j = 0; j < myHeader[3]; ++j) + { + uInt32 bank = myHeader[16 + j] & 0x03; + uInt32 page = (myHeader[16 + j] >> 2) & 0x07; + uInt8* src = myLoadImages + (image * 8448) + (j * 256); + uInt8 sum = checksum(src, 256) + myHeader[16 + j] + myHeader[64 + j]; + + if(!invalidPageChecksumSeen && (sum != 0x55)) + { + cerr << "WARNING: Some Supercharger page checksums are invalid...\n"; + invalidPageChecksumSeen = true; + } + + // Copy page to Supercharger RAM + memcpy(myImage + (bank * 2048) + (page * 256), src, 256); + } + + // Make sure the "dummy" ROM is installed + setupROM(); + return; + } + } + + // TODO: Should probably switch to an internal ROM routine to display + // this message to the user... + cerr << "ERROR: Supercharger load is missing from ROM image...\n"; +} + diff --git a/stella/src/emucore/CartAR.hxx b/stella/src/emucore/CartAR.hxx new file mode 100644 index 000000000..aa365d738 --- /dev/null +++ b/stella/src/emucore/CartAR.hxx @@ -0,0 +1,150 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartAR.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEAR_HXX +#define CARTRIDGEAR_HXX + +class CartridgeAR; +class M6502High; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the cartridge class for Arcadia (aka Starpath) Supercharger + games. Christopher Salomon provided most of the technical details + used in creating this class. + + The Supercharger has four 2K banks. There are three banks of RAM + and one bank of ROM. All 6K of the RAM can be read and written. + + @author Bradford W. Mott + @version $Id: CartAR.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +*/ +class CartridgeAR : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image and size + + @param image Pointer to the ROM image + @param size The size of the ROM image + */ + CartridgeAR(const uInt8* image, uInt32 size); + + /** + Destructor + */ + virtual ~CartridgeAR(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Notification method invoked by the system right before the + system resets its cycle counter to zero. It may be necessary + to override this method for devices that remember cycle counts. + */ + virtual void systemCyclesReset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + // Handle a change to the bank configuration + void bankConfiguration(uInt8 configuration); + + // Compute the sum of the array of bytes + uInt8 checksum(uInt8* s, uInt16 length); + + // Load the specified load into SC RAM + void loadIntoRAM(uInt8 load); + + // Sets up a "dummy" bootstrap ROM in the ROM bank of the cartridge + void setupROM(); + + private: + // Pointer to the 6502 processor in the system + M6502High* my6502; + + // Indicates the offest within the image for the corresponding bank + uInt32 myImageOffset[2]; + + // The 6K of RAM and 2K of ROM contained in the Supercharger + uInt8 myImage[8192]; + + // The 256 byte header for the current 8448 byte load + uInt8 myHeader[256]; + + // All of the 8448 byte loads associated with the game + uInt8* myLoadImages; + + // Indicates how many 8448 loads there are + uInt8 myNumberOfLoadImages; + + // Indicates if the RAM is write enabled + bool myWriteEnabled; + + // Indicates if the ROM's power is on or off + bool myPower; + + // Indicates when the power was last turned on + Int32 myPowerRomCycle; + + // Indicates the "value" address which was accessed + uInt16 myLastAccess; + + // Indicates the number of distinct accesses when "value" was set + uInt32 myNumberOfDistinctAccesses; + + // Indicates if a write is pending or not + bool myWritePending; +}; +#endif + diff --git a/stella/src/emucore/CartE0.cxx b/stella/src/emucore/CartE0.cxx new file mode 100644 index 000000000..e2250810f --- /dev/null +++ b/stella/src/emucore/CartE0.cxx @@ -0,0 +1,191 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartE0.cxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#include +#include "CartE0.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeE0::CartridgeE0(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 8192; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeE0::~CartridgeE0() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeE0::name() const +{ + return "CartridgeE0"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE0::reset() +{ + // Setup segments to some default slices + segmentZero(4); + segmentOne(5); + segmentTwo(6); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE0::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1000 & mask) == 0) && ((0x1400 & mask) == 0) && + ((0x1800 & mask) == 0) && ((0x1C00 & mask) == 0)); + + // Set the page acessing methods for the first part of the last segment + System::PageAccess access; + access.directPokeBase = 0; + access.device = this; + for(uInt32 i = 0x1C00; i < (0x1FE0U & ~mask); i += (1 << shift)) + { + access.directPeekBase = &myImage[7168 + (i & 0x03FF)]; + mySystem->setPageAccess(i >> shift, access); + } + myCurrentSlice[3] = 7; + + // Set the page accessing methods for the hot spots in the last segment + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + for(uInt32 j = (0x1FE0 & ~mask); j < 0x2000; j += (1 << shift)) + { + mySystem->setPageAccess(j >> shift, access); + } + + // Install some default slices for the other segments + segmentZero(4); + segmentOne(5); + segmentTwo(6); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeE0::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + if((address >= 0x0FE0) && (address <= 0x0FE7)) + { + segmentZero(address & 0x0007); + } + else if((address >= 0x0FE8) && (address <= 0x0FEF)) + { + segmentOne(address & 0x0007); + } + else if((address >= 0x0FF0) && (address <= 0x0FF7)) + { + segmentTwo(address & 0x0007); + } + + return myImage[(myCurrentSlice[address >> 10] << 10) + (address & 0x03FF)]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE0::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + if((address >= 0x0FE0) && (address <= 0x0FE7)) + { + segmentZero(address & 0x0007); + } + else if((address >= 0x0FE8) && (address <= 0x0FEF)) + { + segmentOne(address & 0x0007); + } + else if((address >= 0x0FF0) && (address <= 0x0FF7)) + { + segmentTwo(address & 0x0007); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE0::segmentZero(uInt16 slice) +{ + // Remember the new slice + myCurrentSlice[0] = slice; + uInt16 offset = slice << 10; + uInt16 shift = mySystem->pageShift(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + for(uInt32 address = 0x1000; address < 0x1400; address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x03FF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE0::segmentOne(uInt16 slice) +{ + // Remember the new slice + myCurrentSlice[1] = slice; + uInt16 offset = slice << 10; + uInt16 shift = mySystem->pageShift(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + for(uInt32 address = 0x1400; address < 0x1800; address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x03FF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE0::segmentTwo(uInt16 slice) +{ + // Remember the new slice + myCurrentSlice[2] = slice; + uInt16 offset = slice << 10; + uInt16 shift = mySystem->pageShift(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + for(uInt32 address = 0x1800; address < 0x1C00; address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x03FF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartE0.hxx b/stella/src/emucore/CartE0.hxx new file mode 100644 index 000000000..6a94441ad --- /dev/null +++ b/stella/src/emucore/CartE0.hxx @@ -0,0 +1,121 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartE0.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEE0_HXX +#define CARTRIDGEE0_HXX + +class CartridgeF8; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the cartridge class for Parker Brothers' 8K games. In + this bankswitching scheme the 2600's 4K cartridge address space + is broken into four 1K segments. The desired 1K slice of the + ROM is selected by accessing 1FE0 to 1FE7 for the first 1K. + 1FE8 to 1FEF selects the slice for the second 1K, and 1FF0 to + 1FF8 selects the slice for the third 1K. The last 1K segment + always points to the last 1K of the ROM image. + + @author Bradford W. Mott + @version $Id: CartE0.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +*/ +class CartridgeE0 : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeE0(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeE0(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install the specified slice for segment zero + + @param slice The slice to map into the segment + */ + void segmentZero(uInt16 slice); + + /** + Install the specified slice for segment one + + @param slice The slice to map into the segment + */ + void segmentOne(uInt16 slice); + + /** + Install the specified slice for segment two + + @param slice The slice to map into the segment + */ + void segmentTwo(uInt16 slice); + + private: + // Indicates the slice mapped into each of the four segments + uInt16 myCurrentSlice[4]; + + // The 8K ROM image of the cartridge + uInt8 myImage[8192]; +}; +#endif + diff --git a/stella/src/emucore/CartE7.cxx b/stella/src/emucore/CartE7.cxx new file mode 100644 index 000000000..03ba3eadf --- /dev/null +++ b/stella/src/emucore/CartE7.cxx @@ -0,0 +1,214 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartE7.cxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#include +#include "CartE7.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeE7::CartridgeE7(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 16384; ++addr) + { + myImage[addr] = image[addr]; + } + + // Initialize RAM with random values + Random random; + for(uInt32 i = 0; i < 2048; ++i) + { + myRAM[i] = random.next(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeE7::~CartridgeE7() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeE7::name() const +{ + return "CartridgeE7"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE7::reset() +{ + // Install some default banks for the RAM and first segment + bankRAM(0); + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE7::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1400 & mask) == 0) && ((0x1800 & mask) == 0) && + ((0x1900 & mask) == 0) && ((0x1A00 & mask) == 0)); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FE0 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Setup the second segment to always point to the last ROM slice + for(uInt32 j = 0x1A00; j < (0x1FE0U & ~mask); j += (1 << shift)) + { + access.device = this; + access.directPeekBase = &myImage[7 * 2048 + (j & 0x07FF)]; + access.directPokeBase = 0; + mySystem->setPageAccess(j >> shift, access); + } + myCurrentSlice[1] = 7; + + // Install some default banks for the RAM and first segment + bankRAM(0); + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeE7::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + if((address >= 0x0FE0) && (address <= 0x0FE7)) + { + bank(address & 0x0007); + } + else if((address >= 0x0FE8) && (address <= 0x0FEB)) + { + bankRAM(address & 0x0003); + } + + // NOTE: The following does not handle reading from RAM, however, + // this function should never be called for RAM because of the + // way page accessing has been setup + return myImage[(myCurrentSlice[address >> 11] << 11) + (address & 0x07FF)]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE7::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + if((address >= 0x0FE0) && (address <= 0x0FE7)) + { + bank(address & 0x0007); + } + else if((address >= 0x0FE8) && (address <= 0x0FEB)) + { + bankRAM(address & 0x0003); + } + + // NOTE: This does not handle writing to RAM, however, this + // function should never be called for RAM because of the + // way page accessing has been setup +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE7::bank(uInt16 slice) +{ + // Remember what bank we're in + myCurrentSlice[0] = slice; + uInt16 offset = slice << 11; + uInt16 shift = mySystem->pageShift(); + + // Setup the page access methods for the current bank + if(slice != 7) + { + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into first segment + for(uInt32 address = 0x1000; address < 0x1800; address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x07FF)]; + mySystem->setPageAccess(address >> shift, access); + } + } + else + { + System::PageAccess access; + access.device = this; + + // Set the page accessing method for the 1K slice of RAM writing pages + access.directPeekBase = 0; + access.directPokeBase = 0; + for(uInt32 j = 0x1000; j < 0x1400; j += (1 << shift)) + { + access.directPokeBase = &myRAM[j & 0x03FF]; + mySystem->setPageAccess(j >> shift, access); + } + + // Set the page accessing method for the 1K slice of RAM reading pages + access.directPeekBase = 0; + access.directPokeBase = 0; + for(uInt32 k = 0x1400; k < 0x1800; k += (1 << shift)) + { + access.directPeekBase = &myRAM[k & 0x03FF]; + mySystem->setPageAccess(k >> shift, access); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeE7::bankRAM(uInt16 bank) +{ + // Remember what bank we're in + myCurrentRAM = bank; + uInt16 offset = bank << 8; + uInt16 shift = mySystem->pageShift(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + + // Set the page accessing method for the 256 bytes of RAM writing pages + access.directPeekBase = 0; + access.directPokeBase = 0; + for(uInt32 j = 0x1800; j < 0x1900; j += (1 << shift)) + { + access.directPokeBase = &myRAM[1024 + offset + (j & 0x00FF)]; + mySystem->setPageAccess(j >> shift, access); + } + + // Set the page accessing method for the 256 bytes of RAM reading pages + access.directPeekBase = 0; + access.directPokeBase = 0; + for(uInt32 k = 0x1900; k < 0x1A00; k += (1 << shift)) + { + access.directPeekBase = &myRAM[1024 + offset + (k & 0x00FF)]; + mySystem->setPageAccess(k >> shift, access); + } +} + diff --git a/stella/src/emucore/CartE7.hxx b/stella/src/emucore/CartE7.hxx new file mode 100644 index 000000000..6ead04794 --- /dev/null +++ b/stella/src/emucore/CartE7.hxx @@ -0,0 +1,137 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartE7.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEE7_HXX +#define CARTRIDGEE7_HXX + +class CartridgeE7; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the cartridge class for M-Network bankswitched games. + In this bankswitching scheme the 2600's 4K cartridge address + space is broken into two 2K segments. + + Kevin Horton describes E7 as follows: + + Only M-Network used this scheme. This has to be the + most complex method used in any cart! :-) It allows + for the capability of 2K of RAM; although it doesn't + have to be used (in fact, only one cart used it). + There are now 8 2K banks, instead of 4. The last 2K + in the cart always points to the last 2K of the ROM + image, while the first 2K is selectable. You access + 1FE0 to 1FE6 to select which 2K bank. Note that you + cannot select the last 2K of the ROM image into the + lower 2K of the cart! Accessing 1FE7 selects 1K of + RAM at 1000-17FF instead of ROM! The 2K of RAM is + broken up into two 1K sections. One 1K section is + mapped in at 1000-17FF if 1FE7 has been accessed. + 1000-13FF is the write port, while 1400-17FF is the + read port. The second 1K of RAM appears at 1800-19FF. + 1800-18FF is the write port while 1900-19FF is the + read port. You select which 256 byte block appears + here by accessing 1FF8 to 1FFB. + + @author Bradford W. Mott + @version $Id: CartE7.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +*/ +class CartridgeE7 : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeE7(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeE7(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Map the specfied bank into the first segment + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + /** + Install pages for the specified 256 byte bank of RAM + + @param bank The bank that should be installed in the system + */ + void bankRAM(uInt16 bank); + + private: + // Indicates which slice is in the segment + uInt16 myCurrentSlice[2]; + + // Indicates which 256 byte bank of RAM is being used + uInt16 myCurrentRAM; + + // The 16K ROM image of the cartridge + uInt8 myImage[16384]; + + // The 2048 bytes of RAM + uInt8 myRAM[2048]; +}; +#endif + diff --git a/stella/src/emucore/CartF4SC.cxx b/stella/src/emucore/CartF4SC.cxx new file mode 100644 index 000000000..f6dad3ed9 --- /dev/null +++ b/stella/src/emucore/CartF4SC.cxx @@ -0,0 +1,154 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF4SC.cxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#include +#include "CartF4SC.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF4SC::CartridgeF4SC(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 32768; ++addr) + { + myImage[addr] = image[addr]; + } + + // Initialize RAM with random values + Random random; + for(uInt32 i = 0; i < 128; ++i) + { + myRAM[i] = random.next(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF4SC::~CartridgeF4SC() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeF4SC::name() const +{ + return "CartridgeF4SC"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF4SC::reset() +{ + // Upon reset we switch to bank 7 + bank(7); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF4SC::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1080 & mask) == 0) && ((0x1100 & mask) == 0)); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FF4 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Set the page accessing method for the RAM writing pages + for(uInt32 j = 0x1000; j < 0x1080; j += (1 << shift)) + { + access.device = this; + access.directPeekBase = 0; + access.directPokeBase = &myRAM[j & 0x007F]; + mySystem->setPageAccess(j >> shift, access); + } + + // Set the page accessing method for the RAM reading pages + for(uInt32 k = 0x1080; k < 0x1100; k += (1 << shift)) + { + access.device = this; + access.directPeekBase = &myRAM[k & 0x007F]; + access.directPokeBase = 0; + mySystem->setPageAccess(k >> shift, access); + } + + // Install pages for bank 7 + bank(7); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeF4SC::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + if((address >= 0x0FF4) && (address <= 0x0FFB)) + { + bank(address - 0x0FF4); + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup + return myImage[myCurrentBank * 4096 + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF4SC::poke(uInt16 address, uInt8) +{ + // Switch banks if necessary + if((address >= 0x0FF4) && (address <= 0x0FFB)) + { + bank(address - 0x0FF4); + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF4SC::bank(uInt16 bank) +{ + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank * 4096; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1100; address < (0x1FF4U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartF4SC.hxx b/stella/src/emucore/CartF4SC.hxx new file mode 100644 index 000000000..3366d9971 --- /dev/null +++ b/stella/src/emucore/CartF4SC.hxx @@ -0,0 +1,105 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF4SC.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEF4SC_HXX +#define CARTRIDGEF4SC_HXX + +class CartridgeF4SC; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Cartridge class used for Atari's 32K bankswitched games with + 128 bytes of RAM. There are eight 4K banks. + + @author Bradford W. Mott + @version $Id: CartF4SC.hxx,v 1.1.1.1 2001-12-27 19:54:19 bwmott Exp $ +*/ +class CartridgeF4SC : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeF4SC(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeF4SC(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install pages for the specified bank in the system + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 16K ROM image of the cartridge + uInt8 myImage[32768]; + + // The 128 bytes of RAM + uInt8 myRAM[128]; +}; +#endif + diff --git a/stella/src/emucore/CartF6.cxx b/stella/src/emucore/CartF6.cxx new file mode 100644 index 000000000..231d85a81 --- /dev/null +++ b/stella/src/emucore/CartF6.cxx @@ -0,0 +1,165 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF6.cxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#include +#include "CartF6.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF6::CartridgeF6(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 16384; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF6::~CartridgeF6() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeF6::name() const +{ + return "CartridgeF6"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6::reset() +{ + // Upon reset we switch to bank 0 + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1000 & mask) == 0); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FF6 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Upon install we'll setup bank 0 + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeF6::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF6: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF7: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF8: + // Set the current bank to the third 4k bank + bank(2); + break; + + case 0x0FF9: + // Set the current bank to the forth 4k bank + bank(3); + break; + + default: + break; + } + + return myImage[myCurrentBank * 4096 + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF6: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF7: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF8: + // Set the current bank to the third 4k bank + bank(2); + break; + + case 0x0FF9: + // Set the current bank to the forth 4k bank + bank(3); + break; + + default: + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6::bank(uInt16 bank) +{ + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank * 4096; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1000; address < (0x1FF6U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartF6.hxx b/stella/src/emucore/CartF6.hxx new file mode 100644 index 000000000..fcf595653 --- /dev/null +++ b/stella/src/emucore/CartF6.hxx @@ -0,0 +1,102 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF6.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEF6_HXX +#define CARTRIDGEF6_HXX + +class CartridgeF6; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Cartridge class used for Atari's 16K bankswitched games. There + are four 4K banks. + + @author Bradford W. Mott + @version $Id: CartF6.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +*/ +class CartridgeF6 : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeF6(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeF6(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install pages for the specified bank in the system + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 16K ROM image of the cartridge + uInt8 myImage[16384]; +}; +#endif + diff --git a/stella/src/emucore/CartF6SC.cxx b/stella/src/emucore/CartF6SC.cxx new file mode 100644 index 000000000..4e8982481 --- /dev/null +++ b/stella/src/emucore/CartF6SC.cxx @@ -0,0 +1,198 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF6SC.cxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#include +#include "CartF6SC.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF6SC::CartridgeF6SC(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 16384; ++addr) + { + myImage[addr] = image[addr]; + } + + // Initialize RAM with random values + Random random; + for(uInt32 i = 0; i < 128; ++i) + { + myRAM[i] = random.next(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF6SC::~CartridgeF6SC() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeF6SC::name() const +{ + return "CartridgeF6SC"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6SC::reset() +{ + // Upon reset we switch to bank 0 + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6SC::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1080 & mask) == 0) && ((0x1100 & mask) == 0)); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FF6 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Set the page accessing method for the RAM writing pages + for(uInt32 j = 0x1000; j < 0x1080; j += (1 << shift)) + { + access.device = this; + access.directPeekBase = 0; + access.directPokeBase = &myRAM[j & 0x007F]; + mySystem->setPageAccess(j >> shift, access); + } + + // Set the page accessing method for the RAM reading pages + for(uInt32 k = 0x1080; k < 0x1100; k += (1 << shift)) + { + access.device = this; + access.directPeekBase = &myRAM[k & 0x007F]; + access.directPokeBase = 0; + mySystem->setPageAccess(k >> shift, access); + } + + // Install pages for bank 0 + bank(0); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeF6SC::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF6: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF7: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF8: + // Set the current bank to the third 4k bank + bank(2); + break; + + case 0x0FF9: + // Set the current bank to the forth 4k bank + bank(3); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup + return myImage[myCurrentBank * 4096 + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6SC::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF6: + // Set the current bank to the first 4k bank + bank(0); + break; + + case 0x0FF7: + // Set the current bank to the second 4k bank + bank(1); + break; + + case 0x0FF8: + // Set the current bank to the third 4k bank + bank(2); + break; + + case 0x0FF9: + // Set the current bank to the forth 4k bank + bank(3); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF6SC::bank(uInt16 bank) +{ + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank * 4096; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1100; address < (0x1FF6U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartF6SC.hxx b/stella/src/emucore/CartF6SC.hxx new file mode 100644 index 000000000..4453b65a1 --- /dev/null +++ b/stella/src/emucore/CartF6SC.hxx @@ -0,0 +1,105 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF6SC.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEF6SC_HXX +#define CARTRIDGEF6SC_HXX + +class CartridgeF6SC; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Cartridge class used for Atari's 16K bankswitched games with + 128 bytes of RAM. There are four 4K banks. + + @author Bradford W. Mott + @version $Id: CartF6SC.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +*/ +class CartridgeF6SC : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeF6SC(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeF6SC(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install pages for the specified bank in the system + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 16K ROM image of the cartridge + uInt8 myImage[16384]; + + // The 128 bytes of RAM + uInt8 myRAM[128]; +}; +#endif + diff --git a/stella/src/emucore/CartF8.cxx b/stella/src/emucore/CartF8.cxx new file mode 100644 index 000000000..36d8231f4 --- /dev/null +++ b/stella/src/emucore/CartF8.cxx @@ -0,0 +1,145 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF8.cxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#include +#include "CartF8.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF8::CartridgeF8(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 8192; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF8::~CartridgeF8() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeF8::name() const +{ + return "CartridgeF8"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8::reset() +{ + // Upon reset we switch to bank 1 + bank(1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1000 & mask) == 0); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FF8 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Install pages for bank 1 + bank(1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeF8::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF8: + // Set the current bank to the lower 4k bank + bank(0); + break; + + case 0x0FF9: + // Set the current bank to the upper 4k bank + bank(1); + break; + + default: + break; + } + + return myImage[myCurrentBank * 4096 + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF8: + // Set the current bank to the lower 4k bank + bank(0); + break; + + case 0x0FF9: + // Set the current bank to the upper 4k bank + bank(1); + break; + + default: + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8::bank(uInt16 bank) +{ + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank * 4096; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1000; address < (0x1FF8U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartF8.hxx b/stella/src/emucore/CartF8.hxx new file mode 100644 index 000000000..7b2733ebc --- /dev/null +++ b/stella/src/emucore/CartF8.hxx @@ -0,0 +1,102 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF8.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEF8_HXX +#define CARTRIDGEF8_HXX + +class CartridgeF8; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Cartridge class used for Atari's 8K bankswitched games. There + are two 4K banks. + + @author Bradford W. Mott + @version $Id: CartF8.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +*/ +class CartridgeF8 : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeF8(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeF8(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install pages for the specified bank in the system + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 8K ROM image of the cartridge + uInt8 myImage[8192]; +}; +#endif + diff --git a/stella/src/emucore/CartF8SC.cxx b/stella/src/emucore/CartF8SC.cxx new file mode 100644 index 000000000..13b6e164c --- /dev/null +++ b/stella/src/emucore/CartF8SC.cxx @@ -0,0 +1,178 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF8SC.cxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#include +#include "CartF8SC.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF8SC::CartridgeF8SC(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 8192; ++addr) + { + myImage[addr] = image[addr]; + } + + // Initialize RAM with random values + Random random; + for(uInt32 i = 0; i < 128; ++i) + { + myRAM[i] = random.next(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeF8SC::~CartridgeF8SC() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeF8SC::name() const +{ + return "CartridgeF8SC"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8SC::reset() +{ + // Upon reset we switch to bank 1 + bank(1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8SC::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1080 & mask) == 0) && ((0x1100 & mask) == 0)); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FF8 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Set the page accessing method for the RAM writing pages + for(uInt32 j = 0x1000; j < 0x1080; j += (1 << shift)) + { + access.device = this; + access.directPeekBase = 0; + access.directPokeBase = &myRAM[j & 0x007F]; + mySystem->setPageAccess(j >> shift, access); + } + + // Set the page accessing method for the RAM reading pages + for(uInt32 k = 0x1080; k < 0x1100; k += (1 << shift)) + { + access.device = this; + access.directPeekBase = &myRAM[k & 0x007F]; + access.directPokeBase = 0; + mySystem->setPageAccess(k >> shift, access); + } + + // Install pages for bank 1 + bank(1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeF8SC::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF8: + // Set the current bank to the lower 4k bank + bank(0); + break; + + case 0x0FF9: + // Set the current bank to the upper 4k bank + bank(1); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup + return myImage[myCurrentBank * 4096 + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8SC::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF8: + // Set the current bank to the lower 4k bank + bank(0); + break; + + case 0x0FF9: + // Set the current bank to the upper 4k bank + bank(1); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeF8SC::bank(uInt16 bank) +{ + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank << 12; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1100; address < (0x1FF8U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartF8SC.hxx b/stella/src/emucore/CartF8SC.hxx new file mode 100644 index 000000000..0ccc435f8 --- /dev/null +++ b/stella/src/emucore/CartF8SC.hxx @@ -0,0 +1,105 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartF8SC.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEF8SC_HXX +#define CARTRIDGEF8SC_HXX + +class CartridgeF8SC; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Cartridge class used for Atari's 8K bankswitched games with + 128 bytes of RAM. There are two 4K banks. + + @author Bradford W. Mott + @version $Id: CartF8SC.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +*/ +class CartridgeF8SC : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeF8SC(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeF8SC(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install pages for the specified bank in the system + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 8K ROM image of the cartridge + uInt8 myImage[8192]; + + // The 128 bytes of RAM + uInt8 myRAM[128]; +}; +#endif + diff --git a/stella/src/emucore/CartFASC.cxx b/stella/src/emucore/CartFASC.cxx new file mode 100644 index 000000000..8c9e2e1e2 --- /dev/null +++ b/stella/src/emucore/CartFASC.cxx @@ -0,0 +1,188 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartFASC.cxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#include +#include "CartFASC.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeFASC::CartridgeFASC(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 12288; ++addr) + { + myImage[addr] = image[addr]; + } + + // Initialize RAM with random values + Random random; + for(uInt32 i = 0; i < 256; ++i) + { + myRAM[i] = random.next(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeFASC::~CartridgeFASC() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeFASC::name() const +{ + return "CartridgeFASC"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFASC::reset() +{ + // Upon reset we switch to bank 2 + bank(2); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFASC::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1100 & mask) == 0) && ((0x1200 & mask) == 0)); + + // Set the page accessing methods for the hot spots + System::PageAccess access; + for(uInt32 i = (0x1FF8 & ~mask); i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Set the page accessing method for the RAM writing pages + for(uInt32 j = 0x1000; j < 0x1100; j += (1 << shift)) + { + access.device = this; + access.directPeekBase = 0; + access.directPokeBase = &myRAM[j & 0x00FF]; + mySystem->setPageAccess(j >> shift, access); + } + + // Set the page accessing method for the RAM reading pages + for(uInt32 k = 0x1100; k < 0x1200; k += (1 << shift)) + { + access.device = this; + access.directPeekBase = &myRAM[k & 0x00FF]; + access.directPokeBase = 0; + mySystem->setPageAccess(k >> shift, access); + } + + // Install pages for bank 2 + bank(2); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeFASC::peek(uInt16 address) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF8: + // Set the current bank to the lower 4k bank + bank(0); + break; + + case 0x0FF9: + // Set the current bank to the middle 4k bank + bank(1); + break; + + case 0x0FFA: + // Set the current bank to the upper 4k bank + bank(2); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup + return myImage[myCurrentBank * 4096 + address]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFASC::poke(uInt16 address, uInt8) +{ + address = address & 0x0FFF; + + // Switch banks if necessary + switch(address) + { + case 0x0FF8: + // Set the current bank to the lower 4k bank + bank(0); + break; + + case 0x0FF9: + // Set the current bank to the middle 4k bank + bank(1); + break; + + case 0x0FFA: + // Set the current bank to the upper 4k bank + bank(2); + break; + + default: + break; + } + + // NOTE: This does not handle accessing RAM, however, this function + // should never be called for RAM because of the way page accessing + // has been setup +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFASC::bank(uInt16 bank) +{ + // Remember what bank we're in + myCurrentBank = bank; + uInt16 offset = myCurrentBank * 4096; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Setup the page access methods for the current bank + System::PageAccess access; + access.device = this; + access.directPokeBase = 0; + + // Map ROM image into the system + for(uInt32 address = 0x1200; address < (0x1FF8U & ~mask); + address += (1 << shift)) + { + access.directPeekBase = &myImage[offset + (address & 0x0FFF)]; + mySystem->setPageAccess(address >> shift, access); + } +} + diff --git a/stella/src/emucore/CartFASC.hxx b/stella/src/emucore/CartFASC.hxx new file mode 100644 index 000000000..022c66286 --- /dev/null +++ b/stella/src/emucore/CartFASC.hxx @@ -0,0 +1,105 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartFASC.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEFASC_HXX +#define CARTRIDGEFASC_HXX + +class CartridgeFASC; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Cartridge class used for CBS' RAM Plus cartridges. There are + three 4K banks and 256 bytes of RAM. + + @author Bradford W. Mott + @version $Id: CartFASC.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +*/ +class CartridgeFASC : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeFASC(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeFASC(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + /** + Install pages for the specified bank in the system + + @param bank The bank that should be installed in the system + */ + void bank(uInt16 bank); + + private: + // Indicates which bank is currently active + uInt16 myCurrentBank; + + // The 12K ROM image of the cartridge + uInt8 myImage[12288]; + + // The 256 bytes of RAM on the cartridge + uInt8 myRAM[256]; +}; +#endif + diff --git a/stella/src/emucore/CartFE.cxx b/stella/src/emucore/CartFE.cxx new file mode 100644 index 000000000..ee0108024 --- /dev/null +++ b/stella/src/emucore/CartFE.cxx @@ -0,0 +1,81 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartFE.cxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#include +#include "CartFE.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeFE::CartridgeFE(const uInt8* image) +{ + // Copy the ROM image into my buffer + for(uInt32 addr = 0; addr < 8192; ++addr) + { + myImage[addr] = image[addr]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeFE::~CartridgeFE() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeFE::name() const +{ + return "CartridgeFE"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFE::reset() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFE::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1000 & mask) == 0); + + // Map all of the accesses to call peek and poke + System::PageAccess access; + for(uInt32 i = 0x1000; i < 0x2000; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeFE::peek(uInt16 address) +{ + // The bank is determined by A13 of the processor + return myImage[(address & 0x0FFF) + (((address & 0x2000) == 0) ? 4096 : 0)]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeFE::poke(uInt16, uInt8) +{ +} + diff --git a/stella/src/emucore/CartFE.hxx b/stella/src/emucore/CartFE.hxx new file mode 100644 index 000000000..2fc8abbe7 --- /dev/null +++ b/stella/src/emucore/CartFE.hxx @@ -0,0 +1,103 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartFE.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEFE_HXX +#define CARTRIDGEFE_HXX + +class CartridgeFE; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + Bankswitching method used by Activison's Robot Tank and Decathlon. + + Kevin Horton describes FE as follows: + + Used only on two carts (Robot Tank and Decathlon). These + carts are very weird. It does not use accesses to the stack + like was previously thought. Instead, if you watch the called + addresses very carefully, you can see that they are either Dxxx + or Fxxx. This determines the bank to use. Just monitor A13 of + the processor and use it to determine your bank! :-) Of course + the 6507 in the 2600 does not have an A13, so the cart must have + an extra bit in the ROM matrix to tell when to switch banks. + There is *no* way to determine which bank you want to be in from + monitoring the bus. + + @author Bradford W. Mott + @version $Id: CartFE.hxx,v 1.1.1.1 2001-12-27 19:54:20 bwmott Exp $ +*/ +class CartridgeFE : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + */ + CartridgeFE(const uInt8* image); + + /** + Destructor + */ + virtual ~CartridgeFE(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + // The 8K ROM image of the cartridge + uInt8 myImage[8192]; +}; +#endif + diff --git a/stella/src/emucore/CartMC.cxx b/stella/src/emucore/CartMC.cxx new file mode 100644 index 000000000..d47e2d29b --- /dev/null +++ b/stella/src/emucore/CartMC.cxx @@ -0,0 +1,218 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartMC.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include +#include "CartMC.hxx" +#include "Random.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeMC::CartridgeMC(const uInt8* image, uInt32 size) + : mySlot3Locked(false) +{ + uInt32 i; + + // Make sure size is reasonable + assert(size <= 128 * 1024); + + // Allocate array for the cart's RAM + myRAM = new uInt8[32 * 1024]; + + // Initialize RAM with random values + Random random; + for(i = 0; i < 32 * 1024; ++i) + { + myRAM[i] = random.next(); + } + + // Allocate array for the ROM image + myImage = new uInt8[128 * 1024]; + + // Set the contents of the entire ROM to 0 + for(i = 0; i < 128 * 1024; ++i) + { + myImage[i] = 0; + } + + // Copy the ROM image to the end of the ROM buffer + for(i = 0; i < size; ++i) + { + myImage[128 * 1024 - size + i] = image[i]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeMC::~CartridgeMC() +{ + delete[] myRAM; + delete[] myImage; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* CartridgeMC::name() const +{ + return "CartridgeMC"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMC::reset() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMC::install(System& system) +{ + mySystem = &system; + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert(((0x1000 & mask) == 0) && ((0x1400 & mask) == 0) && + ((0x1800 & mask) == 0) && ((0x1C00 & mask) == 0)); + + // Set the page accessing methods for the hot spots in the TIA. For + // correct emulation I would need to chain any accesses below 0x40 to + // the TIA but for now I'll just forget about them. + // + // TODO: These TIA accesses may need to be chained, however, at this + // point Chris isn't sure if the hardware will allow it or not + // + System::PageAccess access; + for(uInt32 i = 0x00; i < 0x40; i += (1 << shift)) + { + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + mySystem->setPageAccess(i >> shift, access); + } + + // Map the cartridge into the system + for(uInt32 j = 0x1000; j < 0x2000; j += (1 << shift)) + { + access.device = this; + access.directPeekBase = 0; + access.directPokeBase = 0; + mySystem->setPageAccess(j >> shift, access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeMC::peek(uInt16 address) +{ + address = address & 0x1FFF; + + // Accessing the RESET vector so lets handle the powerup special case + if((address == 0x1FFC) || (address == 0x1FFD)) + { + // Indicate that slot 3 is locked for now + mySlot3Locked = true; + } + // Should we unlock slot 3? + else if(mySlot3Locked && (address >= 0x1000) && (address <= 0x1BFF)) + { + // Indicate that slot 3 is unlocked now + mySlot3Locked = false; + } + + // Handle reads made to the TIA addresses + if(address < 0x1000) + { + return 0; + } + else + { + uInt8 block; + + if(mySlot3Locked && ((address & 0x0C00) == 0x0C00)) + { + block = 0xFF; + } + else + { + block = myCurrentBlock[(address & 0x0C00) >> 10]; + } + + // Is this a RAM or a ROM access + if(block & 0x80) + { + // ROM access + return myImage[(uInt32)(block & 0x7F) * 1024 + (address & 0x03FF)]; + } + else + { + // This is a RAM access, however, is it to the read or write port? + if(address & 0x0200) + { + // Reading from the read port of the RAM block + return myRAM[(uInt32)(block & 0x3F) * 512 + (address & 0x01FF)]; + } + else + { + // Oops, reading from the write port of the RAM block! + myRAM[(uInt32)(block & 0x3F) * 512 + (address & 0x01FF)] = 0; + return 0; + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMC::poke(uInt16 address, uInt8 value) +{ + address = address & 0x1FFF; + + // Accessing the RESET vector so lets handle the powerup special case + if((address == 0x1FFC) || (address == 0x1FFD)) + { + // Indicate that slot 3 is locked for now + mySlot3Locked = true; + } + // Should we unlock slot 3? + else if(mySlot3Locked && (address >= 0x1000) && (address <= 0x1BFF)) + { + // Indicate that slot 3 is unlocked now + mySlot3Locked = false; + } + + // Handle bank-switching writes + if((address >= 0x003C) && (address <= 0x003F)) + { + myCurrentBlock[address - 0x003C] = value; + } + else + { + uInt8 block; + + if(mySlot3Locked && ((address & 0x0C00) == 0x0C00)) + { + block = 0xFF; + } + else + { + block = myCurrentBlock[(address & 0x0C00) >> 10]; + } + + // Is this a RAM write access + if(!(block & 0x80) && !(address & 0x0200)) + { + // Handle the write to RAM + myRAM[(uInt32)(block & 0x3F) * 512 + (address & 0x01FF)] = value; + } + } +} + diff --git a/stella/src/emucore/CartMC.hxx b/stella/src/emucore/CartMC.hxx new file mode 100644 index 000000000..529b0f71f --- /dev/null +++ b/stella/src/emucore/CartMC.hxx @@ -0,0 +1,207 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: CartMC.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#ifndef CARTRIDGEMC_HXX +#define CARTRIDGEMC_HXX + +class CartridgeMC; + +#include "bspf.hxx" +#include "Cart.hxx" + +/** + This is the cartridge class for Chris Wilkson's Megacart. It does not + handle battery-backed RAM at this time and the code could use some serious + speed improvements. It is based on the following Megacart specification: + + + Megacart Specification, Rev1.1 + (c) 1997 Chris Wilkson + cwilkson@mit.edu + + Description + ----------- + + The Megacart is an external memory cartridge for the Atari 2600 and compatible + home video game consoles. It plugs into the standard cartridge port, and + contains a total of 128K bytes of ROM storage and 32K bytes of battery-backed + RAM storage. + + General Operation + ----------------- + + The Megacart uses "bank switching" to fit the 160K bytes of physical memory + into the console's available 4K address space. Physical memory is divided + into 64 RAM blocks of 512 bytes each, and 128 ROM blocks of 1K bytes each. + RAM blocks are numbered $00 through $3F, and ROM blocks are numbered $80 + through $FF. + + The console's address space is divided into 4 slots of 1K each. Any physical + memory block can be switched into any memory slot by writing its block number + to the "hot address" for the desired slot. Memory locations $3C through $3F + serve as "hot addresses" for memory slots 0 through 3, respectively. + + + Example: + + To make ROM addresses $1A400-$1A7FF (block $E9) available to the console at + memory locations $F800-$FBFF (slot 2), write $E9 to memory location $3e. + + Caution: + + Note that these memory locations are write only. Trying to read the contents + of memory locations $3C through $3F will not only return invalid data, but + will also corrupt the contents causing the software to crash. Reading these + addresses should not be attempted. + + Special Case - RAM + ------------------- + + RAM blocks differ from ROM blocks in that one of the console's address lines, + A9 in this case, must be used as a read/write select. Because of this, RAM + blocks are limited to 512 bytes each, yet still occupy an entire 1K slot. + To store a value A9 must be low. To retrieve a value A9 must high. + + Example: + + First, let's set slot 0 (console addresses $F000-$F3FF) to point to RAM + block $9 (RAM $1200-$13ff). To do this, write $9 to console address $3c. + To store the value $69 in RAM location $1234, write $69 to console address + $F034 (A9=0). To retrieve the value of RAM location $1234, read from console + address $F234 (A9=1). + + Special Case - Powerup + ----------------------- + + Because the console's memory is randomized at powerup, there is no way to + predict the data initially contained in the "hot addresses". Therefore, + hardware will force slot 3 to always point to ROM block $FF immediately + after any read or write to the RESET vector at $FFFC-$FFFD. Block $FF + must contain code to initialize the 4 memory slots to point to the desired + physical memory blocks before any other code can be executed. After program + execution jumps out of the boot code, the hardware will release slot 3 and + it will function just like any other slot. + + Example (the first column is the physical ROM address): + + $00C00 JUNK ... ; random code and data + ... + ... + ... + ... + $1F400 START ... ; program starts here + ... ; slot 3 now points to rom block $83 + ... + ... + ... + $1FFDD BOOT SEI ; disable interrupts + $1FFDE CLD ; set hexadecimal arithmetic mode + $1FFDF LDX #$FF ; + $1FFE1 TXS ; set stack pointer to $ff + $1FFE2 LDA #$00 + $1FFE4 ZERO STA 00,X ; clear RIOT and TIA -BEFORE- setting + $1FFE6 DEX ; up banks + $1FFE7 BNE ZERO + $1FFE9 BANKS LDA #$00 ; ram block 0 ($0000-$01ff) + $1FFEB STA SLOT0 ; slot 0 points to ram block 0 + $1FFED LDA #$34 ; ram block $34 ($6800-$69ff) + $1FFEF STA SLOT1 ; slot 1 points to ram block $34 + $1FFF1 LDA #$FD ; rom block $fd ($1f400-$1f7ff) + $1FFF3 STA SLOT2 ; slot 2 points to rom block $fd + $1FFF5 LDA #$83 ; rom block $83 ($00C00-$01000) + $1FFF7 STA SLOT3 ; slot 3 points to bootcode + ; (rom block $ff) + ; until jumping out of slot 3 + $1FFF9 JMP $F800 ; jump to slot 2 + $1FFFC RESET .WORD $FFDD ; powerup reset vector + $1FFFE SWI .WORD $FFDD ; software interrupt vector (BRK) + + + @author Bradford W. Mott + @version $Id: CartMC.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +*/ +class CartridgeMC : public Cartridge +{ + public: + /** + Create a new cartridge using the specified image and size. If the + size of the image is less than 128K then the cartridge will pad the + beginning of the 128K ROM with zeros. + + @param image Pointer to the ROM image + @param size The size of the ROM image + */ + CartridgeMC(const uInt8* image, uInt32 size); + + /** + Destructor + */ + virtual ~CartridgeMC(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install cartridge in the specified system. Invoked by the system + when the cartridge is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + // Indicates which block is currently active for the four segments + uInt8 myCurrentBlock[4]; + + // Indicates if slot 3 is locked to block $FF or not + bool mySlot3Locked; + + // Pointer to the 32K bytes of RAM for the cartridge + uInt8* myRAM; + + // Pointer to the 128K bytes of ROM for the cartridge + uInt8* myImage; +}; +#endif + diff --git a/stella/src/emucore/Console.cxx b/stella/src/emucore/Console.cxx new file mode 100644 index 000000000..a4be0ebb3 --- /dev/null +++ b/stella/src/emucore/Console.cxx @@ -0,0 +1,261 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Console.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include +#include "Booster.hxx" +#include "Cart.hxx" +#include "Console.hxx" +#include "Control.hxx" +#include "Driving.hxx" +#include "Event.hxx" +#include "Joystick.hxx" +#include "Keyboard.hxx" +#include "M6502Low.hxx" +#include "M6502Hi.hxx" +#include "M6532.hxx" +#include "MD5.hxx" +#include "MediaSrc.hxx" +#include "Paddles.hxx" +#include "Props.hxx" +#include "PropsSet.hxx" +#include "Switches.hxx" +#include "System.hxx" +#include "TIA.hxx" + +/** + Compare the two strings s1 and s2 ignoring the case of the + characters. Answers true iff they are equal. + + @param s1 The first string to compare + @param s2 The second string to compare + @return true iff the two strings are equal +*/ +static bool compare(const string& s1, const string& s2) +{ + if(s1.length() != s2.length()) + { + return false; + } + + for(uInt32 i = 0; i < s1.length(); ++i) + { + if(tolower(s1[i]) != tolower(s2[i])) + { + return false; + } + } + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Console::Console(const uInt8* image, uInt32 size, const char* filename, + const Event& event, PropertiesSet& propertiesSet, Sound& sound) + : myEvent(event) +{ + myControllers[0] = 0; + myControllers[1] = 0; + myMediaSource = 0; + mySwitches = 0; + mySystem = 0; + myProperties = defaultProperties(); + + // Get the MD5 message-digest for the ROM image + string md5 = MD5(image, size); + + // Search through the properties set to see if some exist for this game + for(uInt32 i = 0; i < propertiesSet.size(); ++i) + { + const Properties& properties = propertiesSet.get(i); + + if(properties.get("Cartridge.MD5") == md5) + { + // We have a match so let's use those properties + myProperties = properties; + break; + } + } + + // If there was no MD5 match then let's search based on filename + if(md5 != myProperties.get("Cartridge.MD5")) + { + for(uInt32 i = 0; i < propertiesSet.size(); ++i) + { + const Properties& properties = propertiesSet.get(i); + + if(compare(properties.get("Cartridge.Filename"), filename)) + { + // We have a match so let's use those properties + myProperties = properties; + break; + } + } + } + + // TODO: At some point I belive we'll need to set the properties' + // MD5 value so the user will be able to edit it. + // myProperties.save(cout); + + // Setup the controllers based on properties + string left = myProperties.get("Controller.Left"); + string right = myProperties.get("Controller.Right"); + + // Construct left controller + if(left == "Booster-Grip") + { + myControllers[0] = new BoosterGrip(Controller::Left, myEvent); + } + else if(left == "Driving") + { + myControllers[0] = new Driving(Controller::Left, myEvent); + } + else if((left == "Keyboard") || (left == "Keypad")) + { + myControllers[0] = new Keyboard(Controller::Left, myEvent); + } + else if(left == "Paddles") + { + myControllers[0] = new Paddles(Controller::Left, myEvent); + } + else + { + myControllers[0] = new Joystick(Controller::Left, myEvent); + } + + // Construct right controller + if(right == "Booster-Grip") + { + myControllers[1] = new BoosterGrip(Controller::Right, myEvent); + } + else if(right == "Driving") + { + myControllers[1] = new Driving(Controller::Right, myEvent); + } + else if((right == "Keyboard") || (right == "Keypad")) + { + myControllers[1] = new Keyboard(Controller::Right, myEvent); + } + else if(right == "Paddles") + { + myControllers[1] = new Paddles(Controller::Right, myEvent); + } + else + { + myControllers[1] = new Joystick(Controller::Right, myEvent); + } + + // Create switches for the console + mySwitches = new Switches(myEvent, myProperties); + + // Now, we can construct the system and components + mySystem = new System(13, 6); + + M6502* m6502; + if((myProperties.get("Emulation.CPU") == "High") || + ((myProperties.get("Emulation.CPU") == "Auto-detect") && !(size % 8448))) + { + m6502 = new M6502High(1); + } + else + { + m6502 = new M6502Low(1); + } + + M6532* m6532 = new M6532(*this); + TIA* tia = new TIA(*this, sound); + Cartridge* cartridge = Cartridge::create(image, size, myProperties); + + mySystem->attach(m6502); + mySystem->attach(m6532); + mySystem->attach(tia); + mySystem->attach(cartridge); + + // Remember what my media source is + myMediaSource = tia; + + // Reset, the system to its power-on state + mySystem->reset(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Console::Console(const Console& console) + : myEvent(console.myEvent) +{ + // TODO: Write this method + assert(false); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Console::~Console() +{ + delete mySystem; + delete mySwitches; + delete myControllers[0]; + delete myControllers[1]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Properties& Console::properties() const +{ + return myProperties; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Console& Console::operator = (const Console&) +{ + // TODO: Write this method + assert(false); + + return *this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Properties& Console::defaultProperties() +{ + // Make sure the pairs are in the default properties object + ourDefaultProperties.set("Cartridge.Filename", ""); + ourDefaultProperties.set("Cartridge.MD5", ""); + ourDefaultProperties.set("Cartridge.Manufacturer", ""); + ourDefaultProperties.set("Cartridge.ModelNo", ""); + ourDefaultProperties.set("Cartridge.Name", "Untitled"); + ourDefaultProperties.set("Cartridge.Note", ""); + ourDefaultProperties.set("Cartridge.Rarity", ""); + ourDefaultProperties.set("Cartridge.Type", "Auto-detect"); + + ourDefaultProperties.set("Console.LeftDifficulty", "B"); + ourDefaultProperties.set("Console.RightDifficulty", "B"); + ourDefaultProperties.set("Console.TelevisionType", "Color"); + + ourDefaultProperties.set("Controller.Left", "Joystick"); + ourDefaultProperties.set("Controller.Right", "Joystick"); + + ourDefaultProperties.set("Display.Format", "NTSC"); + ourDefaultProperties.set("Display.XStart", "0"); + ourDefaultProperties.set("Display.Width", "160"); + ourDefaultProperties.set("Display.YStart", "38"); + ourDefaultProperties.set("Display.Height", "210"); + + ourDefaultProperties.set("Emulation.CPU", "Auto-detect"); + ourDefaultProperties.set("Emulation.HmoveBlanks", "Yes"); + + return ourDefaultProperties; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Properties Console::ourDefaultProperties; + diff --git a/stella/src/emucore/Console.hxx b/stella/src/emucore/Console.hxx new file mode 100644 index 000000000..338f9d3f4 --- /dev/null +++ b/stella/src/emucore/Console.hxx @@ -0,0 +1,159 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Console.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#ifndef CONSOLE_HXX +#define CONSOLE_HXX + +class Console; +class Controller; +class Event; +class MediaSource; +class PropertiesSet; +class Sound; +class Switches; +class System; + +#include "bspf.hxx" +#include "Control.hxx" +#include "Props.hxx" + +/** + This class represents the entire game console. + + @author Bradford W. Mott + @version $Id: Console.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +*/ +class Console +{ + public: + /** + Create a new console for emulating the specified game using the + given event object and game profiles. + + @param image The ROM image of the game to emulate + @param size The size of the ROM image + @param filename The name of the file that contained the ROM image + @param event The event object to use + @param profiles The game profiles object to use + @param sound The sound object to use + */ + Console(const uInt8* image, uInt32 size, const char* filename, + const Event& event, PropertiesSet& propertiesSet, Sound& sound); + + /** + Create a new console object by copying another one + + @param console The object to copy + */ + Console(const Console& console); + + /** + Destructor + */ + virtual ~Console(); + + public: + /** + Get the controller plugged into the specified jack + + @return The specified controller + */ + Controller& controller(Controller::Jack jack) const + { + return (jack == Controller::Left) ? *myControllers[0] : *myControllers[1]; + } + + /** + Get the media source of the console + + @return The media source + */ + MediaSource& mediaSource() const + { + return *myMediaSource; + } + + /** + Get the properties being used by the game + + @return The properties being used by the game + */ + const Properties& properties() const; + + /** + Get the console switches + + @return The console switches + */ + Switches& switches() const + { + return *mySwitches; + } + + /** + Get the 6502 based system used by the console to emulate the game + + @return The 6502 based system + */ + System& system() const + { + return *mySystem; + } + + public: + /** + Overloaded assignment operator + + @param console The console object to set myself equal to + @return Myself after assignment has taken place + */ + Console& operator = (const Console& console); + + public: + /** + Get the default properties object to use for other properties objects + + @return The default properties object + */ + static const Properties& defaultProperties(); + + private: + // Pointers to the left and right controllers + Controller* myControllers[2]; + + // Reference to the event object to use + const Event& myEvent; + + // Pointer to the media source object + MediaSource* myMediaSource; + + // Properties for the game + Properties myProperties; + + // Pointer to the switches on the front of the console + Switches* mySwitches; + + // Pointer to the 6502 based system being emulated + System* mySystem; + + private: + // Default properties to use for properties objects + static Properties ourDefaultProperties; +}; +#endif + diff --git a/stella/src/emucore/Control.cxx b/stella/src/emucore/Control.cxx new file mode 100644 index 000000000..da154b20a --- /dev/null +++ b/stella/src/emucore/Control.cxx @@ -0,0 +1,54 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Control.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include +#include "Control.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Controller::Controller(Jack jack, const Event& event) + : myJack(jack), + myEvent(event) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Controller::~Controller() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Int32 Controller::maximumResistance = 0x7FFFFFFF; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Int32 Controller::minimumResistance = 0x00000000; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Controller::Controller(const Controller& c) + : myJack(c.myJack), + myEvent(c.myEvent) +{ + assert(false); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Controller& Controller::operator = (const Controller&) +{ + assert(false); + return *this; +} + diff --git a/stella/src/emucore/Control.hxx b/stella/src/emucore/Control.hxx new file mode 100644 index 000000000..2f9a0ab9b --- /dev/null +++ b/stella/src/emucore/Control.hxx @@ -0,0 +1,152 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Control.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#ifndef CONTROLLER_HXX +#define CONTROLLER_HXX + +class Controller; +class Event; + +#include "bspf.hxx" + +/** + A controller is a device that plugs into either the left or right + controller jack of the Video Computer System (VCS). The pins of + the controller jacks are mapped as follows: + + ------------- + \ 1 2 3 4 5 / + \ 6 7 8 9 / + --------- + + Left Controller Right Controller + + pin 1 D4 PIA SWCHA D0 PIA SWCHA + pin 2 D5 PIA SWCHA D1 PIA SWCHA + pin 3 D6 PIA SWCHA D2 PIA SWCHA + pin 4 D7 PIA SWCHA D3 PIA SWCHA + pin 5 D7 TIA INPT1 (Dumped) D7 TIA INPT3 (Dumped) + pin 6 D7 TIA INPT4 (Latched) D7 TIA INPT5 (Latched) + pin 7 +5 +5 + pin 8 GND GND + pin 9 D7 TIA INPT0 (Dumped) D7 TIA INPT2 (Dumped) + + Each of the pins connected to the PIA can be configured as an + input or output pin. The "dumped" TIA pins are used to charge + a capacitor. A potentiometer is sometimes connected to these + pins for analog input. + + This is a base class for all controllers. It provides a view + of the controller from the prespective of the controller's jack. + + @author Bradford W. Mott + @version $Id: Control.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +*/ +class Controller +{ + public: + /** + Enumeration of the controller jacks + */ + enum Jack + { + Left, Right + }; + + public: + /** + Create a new controller plugged into the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + */ + Controller(Jack jack, const Event& event); + + /** + Destructor + */ + virtual ~Controller(); + + public: + /** + Enumeration of the digital pins of a controller port + */ + enum DigitalPin + { + One, Two, Three, Four, Six + }; + + /** + Enumeration of the analog pins of a controller port + */ + enum AnalogPin + { + Five, Nine + }; + + public: + /** + Read the value of the specified digital pin for this controller. + + @param pin The pin of the controller jack to read + @return The state of the pin + */ + virtual bool read(DigitalPin pin) = 0; + + /** + Read the resistance at the specified analog pin for this controller. + The returned value is the resistance measured in ohms. + + @param pin The pin of the controller jack to read + @return The resistance at the specified pin + */ + virtual Int32 read(AnalogPin pin) = 0; + + /** + Write the given value to the specified digital pin for this + controller. Writing is only allowed to the pins associated + with the PIA. Therefore you cannot write to pin six. + + @param pin The pin of the controller jack to write to + @param value The value to write to the pin + */ + virtual void write(DigitalPin pin, bool value) = 0; + + public: + /// Constant which represents maximum resistance for analog pins + static const Int32 maximumResistance; + + /// Constant which represents minimum resistance for analog pins + static const Int32 minimumResistance; + + protected: + /// Specifies which jack the controller is plugged in + const Jack myJack; + + /// Reference to the event object this controller uses + const Event& myEvent; + + protected: + // Copy constructor isn't supported by controllers so make it private + Controller(const Controller&); + + // Assignment operator isn't supported by controllers so make it private + Controller& operator = (const Controller&); +}; +#endif + diff --git a/stella/src/emucore/DefProps.cxx b/stella/src/emucore/DefProps.cxx new file mode 100644 index 000000000..009e1ea01 --- /dev/null +++ b/stella/src/emucore/DefProps.cxx @@ -0,0 +1,35 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: DefProps.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include "DefProps.hxx" + +/** + The default properties file is generated from the 'stella.pro' file + using a sed script +*/ +static const char* theScript[] = { + #include "DefProps.def" + 0 +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char** defaultPropertiesFile() +{ + return theScript; +} + diff --git a/stella/src/emucore/DefProps.hxx b/stella/src/emucore/DefProps.hxx new file mode 100644 index 000000000..a1cecdc84 --- /dev/null +++ b/stella/src/emucore/DefProps.hxx @@ -0,0 +1,31 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: DefProps.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#ifndef DEFAULTPROPERTIES_HXX +#define DEFAULTPROPERTIES_HXX + +/** + Get the default properties file as an array of pointers to null-terminated + character arrays. The last entry in the array is the null pointer. + + @return The default properties file +*/ +const char** defaultPropertiesFile(); + +#endif + diff --git a/stella/src/emucore/Driving.cxx b/stella/src/emucore/Driving.cxx new file mode 100644 index 000000000..bd2044bdc --- /dev/null +++ b/stella/src/emucore/Driving.cxx @@ -0,0 +1,128 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Driving.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include +#include "Event.hxx" +#include "Driving.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Driving::Driving(Jack jack, const Event& event) + : Controller(jack, event) +{ + myCounter = 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Driving::~Driving() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Driving::read(DigitalPin pin) +{ + // Gray codes for clockwise rotation + static const uInt8 clockwise[] = { 0x03, 0x01, 0x00, 0x02 }; + + // Gray codes for counter-clockwise rotation + static const uInt8 counterclockwise[] = { 0x03, 0x02, 0x00, 0x01 }; + + // Delay used for moving through the gray code tables + const uInt32 delay = 20; + + switch(pin) + { + case One: + ++myCounter; + + if(myJack == Left) + { + if(myEvent.get(Event::JoystickZeroLeft) != 0) + { + return (counterclockwise[(myCounter / delay) & 0x03] & 0x01) != 0; + } + else if(myEvent.get(Event::JoystickZeroRight) != 0) + { + return (clockwise[(myCounter / delay) & 0x03] & 0x01) != 0; + } + } + else + { + if(myEvent.get(Event::JoystickOneLeft) != 0) + { + return (counterclockwise[(myCounter / delay) & 0x03] & 0x01) != 0; + } + else if(myEvent.get(Event::JoystickOneRight) != 0) + { + return (clockwise[(myCounter / delay) & 0x03] & 0x01) != 0; + } + } + + case Two: + if(myJack == Left) + { + if(myEvent.get(Event::JoystickZeroLeft) != 0) + { + return (counterclockwise[(myCounter / delay) & 0x03] & 0x02) != 0; + } + else if(myEvent.get(Event::JoystickZeroRight) != 0) + { + return (clockwise[(myCounter / delay) & 0x03] & 0x02) != 0; + } + } + else + { + if(myEvent.get(Event::JoystickOneLeft) != 0) + { + return (counterclockwise[(myCounter / delay) & 0x03] & 0x02) != 0; + } + else if(myEvent.get(Event::JoystickOneRight) != 0) + { + return (clockwise[(myCounter / delay) & 0x03] & 0x02) != 0; + } + } + + case Three: + return true; + + case Four: + return true; + + case Six: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroFire) == 0) : + (myEvent.get(Event::JoystickOneFire) == 0); + + default: + return true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 Driving::read(AnalogPin) +{ + // Analog pins are not connect in driving controller so we have + // infinite resistance + return maximumResistance; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Driving::write(DigitalPin, bool) +{ + // Writing doesn't do anything to the driving controller... +} + diff --git a/stella/src/emucore/Driving.hxx b/stella/src/emucore/Driving.hxx new file mode 100644 index 000000000..e8e88f707 --- /dev/null +++ b/stella/src/emucore/Driving.hxx @@ -0,0 +1,84 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Driving.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#ifndef DRIVING_HXX +#define DRIVING_HXX + +class Driving; +class System; + +#include "bspf.hxx" +#include "Control.hxx" + +/** + The standard Atari 2600 Indy 500 driving controller. + + @author Bradford W. Mott + @version $Id: Driving.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +*/ +class Driving : public Controller +{ + public: + /** + Create a new Indy 500 driving controller plugged into + the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + */ + Driving(Jack jack, const Event& event); + + /** + Destructor + */ + virtual ~Driving(); + + public: + /** + Read the value of the specified digital pin for this controller. + + @param pin The pin of the controller jack to read + @return The state of the pin + */ + virtual bool read(DigitalPin pin); + + /** + Read the resistance at the specified analog pin for this controller. + The returned value is the resistance measured in ohms. + + @param pin The pin of the controller jack to read + @return The resistance at the specified pin + */ + virtual Int32 read(AnalogPin pin); + + /** + Write the given value to the specified digital pin for this + controller. Writing is only allowed to the pins associated + with the PIA. Therefore you cannot write to pin six. + + @param pin The pin of the controller jack to write to + @param value The value to write to the pin + */ + virtual void write(DigitalPin pin, bool value); + + private: + // Counter to iterate through the gray codes + uInt32 myCounter; +}; +#endif + diff --git a/stella/src/emucore/Event.cxx b/stella/src/emucore/Event.cxx new file mode 100644 index 000000000..367189bb4 --- /dev/null +++ b/stella/src/emucore/Event.cxx @@ -0,0 +1,48 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Event.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include "Event.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Event::Event() + : myNumberOfTypes(Event::LastType) +{ + // Set all of the events to 0 / false to start with + for(int i = 0; i < myNumberOfTypes; ++i) + { + myValues[i] = 0; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Event::~Event() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 Event::get(Type type) const +{ + return myValues[type]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Event::set(Type type, Int32 value) +{ + myValues[type] = value; +} + diff --git a/stella/src/emucore/Event.hxx b/stella/src/emucore/Event.hxx new file mode 100644 index 000000000..ff36e5ebc --- /dev/null +++ b/stella/src/emucore/Event.hxx @@ -0,0 +1,102 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Event.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#ifndef EVENT_HXX +#define EVENT_HXX + +class Event; + +#include "bspf.hxx" + +/** + @author Bradford W. Mott + @version $Id: Event.hxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +*/ +class Event +{ + public: + /** + Enumeration of console and controller event types + */ + enum Type + { + ConsoleOn, ConsoleOff, ConsoleColor, ConsoleBlackWhite, + ConsoleLeftDifficultyA, ConsoleLeftDifficultyB, + ConsoleRightDifficultyA, ConsoleRightDifficultyB, + ConsoleSelect, ConsoleReset, + + JoystickZeroUp, JoystickZeroDown, JoystickZeroLeft, + JoystickZeroRight, JoystickZeroFire, + JoystickOneUp, JoystickOneDown, JoystickOneLeft, + JoystickOneRight, JoystickOneFire, + + BoosterGripZeroTrigger, BoosterGripZeroBooster, + BoosterGripOneTrigger, BoosterGripOneBooster, + + PaddleZeroResistance, PaddleZeroFire, + PaddleOneResistance, PaddleOneFire, + PaddleTwoResistance, PaddleTwoFire, + PaddleThreeResistance, PaddleThreeFire, + + KeyboardZero1, KeyboardZero2, KeyboardZero3, + KeyboardZero4, KeyboardZero5, KeyboardZero6, + KeyboardZero7, KeyboardZero8, KeyboardZero9, + KeyboardZeroStar, KeyboardZero0, KeyboardZeroPound, + + KeyboardOne1, KeyboardOne2, KeyboardOne3, + KeyboardOne4, KeyboardOne5, KeyboardOne6, + KeyboardOne7, KeyboardOne8, KeyboardOne9, + KeyboardOneStar, KeyboardOne0, KeyboardOnePound, + + DrivingZeroClockwise, DrivingZeroCounterClockwise, DrivingZeroFire, + DrivingOneClockwise, DrivingOneCounterClockwise, DrivingOneFire, + + LastType + }; + + public: + /** + Create a new event object + */ + Event(); + + /** + Destructor + */ + virtual ~Event(); + + public: + /** + Get the value associated with the event of the specified type + */ + virtual Int32 get(Type type) const; + + /** + Set the value associated with the event of the specified type + */ + virtual void set(Type type, Int32 value); + + protected: + // Number of event types there are + const Int32 myNumberOfTypes; + + // Array of values associated with each event type + Int32 myValues[LastType]; +}; +#endif + diff --git a/stella/src/emucore/Joystick.cxx b/stella/src/emucore/Joystick.cxx new file mode 100644 index 000000000..0a070bec1 --- /dev/null +++ b/stella/src/emucore/Joystick.cxx @@ -0,0 +1,76 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Joystick.cxx,v 1.1.1.1 2001-12-27 19:54:21 bwmott Exp $ +//============================================================================ + +#include +#include "Event.hxx" +#include "Joystick.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Joystick::Joystick(Jack jack, const Event& event) + : Controller(jack, event) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Joystick::~Joystick() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Joystick::read(DigitalPin pin) +{ + switch(pin) + { + case One: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroUp) == 0) : + (myEvent.get(Event::JoystickOneUp) == 0); + + case Two: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroDown) == 0) : + (myEvent.get(Event::JoystickOneDown) == 0); + + case Three: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroLeft) == 0) : + (myEvent.get(Event::JoystickOneLeft) == 0); + + case Four: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroRight) == 0) : + (myEvent.get(Event::JoystickOneRight) == 0); + + case Six: + return (myJack == Left) ? (myEvent.get(Event::JoystickZeroFire) == 0) : + (myEvent.get(Event::JoystickOneFire) == 0); + + default: + return true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 Joystick::read(AnalogPin) +{ + // Analog pins are not connect in joystick so we have infinite resistance + return maximumResistance; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Joystick::write(DigitalPin, bool) +{ + // Writing doesn't do anything to the joystick... +} + diff --git a/stella/src/emucore/Joystick.hxx b/stella/src/emucore/Joystick.hxx new file mode 100644 index 000000000..c471d35dc --- /dev/null +++ b/stella/src/emucore/Joystick.hxx @@ -0,0 +1,76 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Joystick.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#ifndef JOYSTICK_HXX +#define JOYSTICK_HXX + +#include "bspf.hxx" +#include "Control.hxx" + +/** + The standard Atari 2600 joystick controller. + + @author Bradford W. Mott + @version $Id: Joystick.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +*/ +class Joystick : public Controller +{ + public: + /** + Create a new joystick controller plugged into the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + */ + Joystick(Jack jack, const Event& event); + + /** + Destructor + */ + virtual ~Joystick(); + + public: + /** + Read the value of the specified digital pin for this controller. + + @param pin The pin of the controller jack to read + @return The state of the pin + */ + virtual bool read(DigitalPin pin); + + /** + Read the resistance at the specified analog pin for this controller. + The returned value is the resistance measured in ohms. + + @param pin The pin of the controller jack to read + @return The resistance at the specified pin + */ + virtual Int32 read(AnalogPin pin); + + /** + Write the given value to the specified digital pin for this + controller. Writing is only allowed to the pins associated + with the PIA. Therefore you cannot write to pin six. + + @param pin The pin of the controller jack to write to + @param value The value to write to the pin + */ + virtual void write(DigitalPin pin, bool value); +}; +#endif + diff --git a/stella/src/emucore/Keyboard.cxx b/stella/src/emucore/Keyboard.cxx new file mode 100644 index 000000000..145e800d3 --- /dev/null +++ b/stella/src/emucore/Keyboard.cxx @@ -0,0 +1,207 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Keyboard.cxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#include "Event.hxx" +#include "Keyboard.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Keyboard::Keyboard(Jack jack, const Event& event) + : Controller(jack, event), + myPinState(0) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Keyboard::~Keyboard() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Keyboard::read(DigitalPin pin) +{ + if(pin == Six) + { + if((myPinState & 0x01) == 0) + { + return (myJack == Left) ? (myEvent.get(Event::KeyboardZero3) == 0) : + (myEvent.get(Event::KeyboardOne3) == 0); + } + else if((myPinState & 0x02) == 0) + { + return (myJack == Left) ? (myEvent.get(Event::KeyboardZero6) == 0) : + (myEvent.get(Event::KeyboardOne6) == 0); + } + else if((myPinState & 0x04) == 0) + { + return (myJack == Left) ? (myEvent.get(Event::KeyboardZero9) == 0) : + (myEvent.get(Event::KeyboardOne9) == 0); + } + else if((myPinState & 0x08) == 0) + { + return (myJack == Left) ? (myEvent.get(Event::KeyboardZeroPound) == 0) : + (myEvent.get(Event::KeyboardOnePound) == 0); + } + } + + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 Keyboard::read(AnalogPin pin) +{ + if(pin == Nine) + { + if((myPinState & 0x01) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero1) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne1) != 0) ? + maximumResistance : minimumResistance; + } + } + else if((myPinState & 0x02) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero4) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne4) != 0) ? + maximumResistance : minimumResistance; + } + } + else if((myPinState & 0x04) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero7) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne7) != 0) ? + maximumResistance : minimumResistance; + } + } + else if((myPinState & 0x08) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZeroStar) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOneStar) != 0) ? + maximumResistance : minimumResistance; + } + } + } + else + { + if((myPinState & 0x01) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero2) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne2) != 0) ? + maximumResistance : minimumResistance; + } + } + else if((myPinState & 0x02) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero5) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne5) != 0) ? + maximumResistance : minimumResistance; + } + } + else if((myPinState & 0x04) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero8) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne8) != 0) ? + maximumResistance : minimumResistance; + } + } + else if((myPinState & 0x08) == 0) + { + if(myJack == Left) + { + return (myEvent.get(Event::KeyboardZero0) != 0) ? + maximumResistance : minimumResistance; + } + else + { + return (myEvent.get(Event::KeyboardOne0) != 0) ? + maximumResistance : minimumResistance; + } + } + } + + return maximumResistance; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Keyboard::write(DigitalPin pin, bool value) +{ + // Change the pin state based on value + switch(pin) + { + case One: + myPinState = (myPinState & 0x0E) | (value ? 0x01 : 0x00); + break; + + case Two: + myPinState = (myPinState & 0x0D) | (value ? 0x02 : 0x00); + break; + + case Three: + myPinState = (myPinState & 0x0B) | (value ? 0x04 : 0x00); + break; + + case Four: + myPinState = (myPinState & 0x07) | (value ? 0x08 : 0x00); + break; + + default: + break; + } +} + diff --git a/stella/src/emucore/Keyboard.hxx b/stella/src/emucore/Keyboard.hxx new file mode 100644 index 000000000..5481227c6 --- /dev/null +++ b/stella/src/emucore/Keyboard.hxx @@ -0,0 +1,80 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Keyboard.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#ifndef KEYBOARD_HXX +#define KEYBOARD_HXX + +#include "bspf.hxx" +#include "Control.hxx" + +/** + The standard Atari 2600 keyboard controller + + @author Bradford W. Mott + @version $Id: Keyboard.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +*/ +class Keyboard : public Controller +{ + public: + /** + Create a new keyboard controller plugged into the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + */ + Keyboard(Jack jack, const Event& event); + + /** + Destructor + */ + virtual ~Keyboard(); + + public: + /** + Read the value of the specified digital pin for this controller. + + @param pin The pin of the controller jack to read + @return The state of the pin + */ + virtual bool read(DigitalPin pin); + + /** + Read the resistance at the specified analog pin for this controller. + The returned value is the resistance measured in ohms. + + @param pin The pin of the controller jack to read + @return The resistance at the specified pin + */ + virtual Int32 read(AnalogPin pin); + + /** + Write the given value to the specified digital pin for this + controller. Writing is only allowed to the pins associated + with the PIA. Therefore you cannot write to pin six. + + @param pin The pin of the controller jack to write to + @param value The value to write to the pin + */ + virtual void write(DigitalPin pin, bool value); + + private: + // State of the output pins + uInt8 myPinState; +}; +#endif + diff --git a/stella/src/emucore/M6532.cxx b/stella/src/emucore/M6532.cxx new file mode 100644 index 000000000..6df99c907 --- /dev/null +++ b/stella/src/emucore/M6532.cxx @@ -0,0 +1,306 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6532.cxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#include +#include "Console.hxx" +#include "M6532.hxx" +#include "Random.hxx" +#include "Switches.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6532::M6532(const Console& console) + : myConsole(console) +{ + // Randomize the 128 bytes of memory + Random random; + + for(uInt32 t = 0; t < 128; ++t) + { + myRAM[t] = random.next(); + } + + // Initialize other data members + reset(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6532::~M6532() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* M6532::name() const +{ + return "M6532"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6532::reset() +{ + myTimer = 100; + myIntervalShift = 6; + myCyclesWhenTimerSet = 0; + myCyclesWhenInterruptReset = 0; + myTimerReadAfterInterrupt = false; + + // Zero the I/O registers + myDDRA = 0x00; + myDDRB = 0x00; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6532::systemCyclesReset() +{ + // System cycles are being reset to zero so we need to adjust + // the cycle count we remembered when the timer was last set + myCyclesWhenTimerSet -= mySystem->cycles(); + myCyclesWhenInterruptReset -= mySystem->cycles(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6532::install(System& system) +{ + // Remember which system I'm installed in + mySystem = &system; + + uInt16 shift = mySystem->pageShift(); + uInt16 mask = mySystem->pageMask(); + + // Make sure the system we're being installed in has a page size that'll work + assert((0x1080 & mask) == 0); + + // All accesses are to this device + System::PageAccess access; + access.device = this; + + // We're installing in a 2600 system + for(int address = 0; address < 8192; address += (1 << shift)) + { + if((address & 0x1080) == 0x0080) + { + if((address & 0x0200) == 0x0000) + { + access.directPeekBase = &myRAM[address & 0x007f]; + access.directPokeBase = &myRAM[address & 0x007f]; + mySystem->setPageAccess(address >> shift, access); + } + else + { + access.directPeekBase = 0; + access.directPokeBase = 0; + mySystem->setPageAccess(address >> shift, access); + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 M6532::peek(uInt16 addr) +{ + switch(addr & 0x07) + { + case 0x00: // Port A I/O Register (Joystick) + { + uInt8 value = 0x00; + + if(myConsole.controller(Controller::Left).read(Controller::One)) + value |= 0x10; + if(myConsole.controller(Controller::Left).read(Controller::Two)) + value |= 0x20; + if(myConsole.controller(Controller::Left).read(Controller::Three)) + value |= 0x40; + if(myConsole.controller(Controller::Left).read(Controller::Four)) + value |= 0x80; + + if(myConsole.controller(Controller::Right).read(Controller::One)) + value |= 0x01; + if(myConsole.controller(Controller::Right).read(Controller::Two)) + value |= 0x02; + if(myConsole.controller(Controller::Right).read(Controller::Three)) + value |= 0x04; + if(myConsole.controller(Controller::Right).read(Controller::Four)) + value |= 0x08; + + return value; + } + + case 0x01: // Port A Data Direction Register + { + return myDDRA; + } + + case 0x02: // Port B I/O Register (Console switches) + { + return myConsole.switches().read(); + } + + case 0x03: // Port B Data Direction Register + { + return myDDRB; + } + + case 0x04: // Timer Output + case 0x06: + { + uInt32 cycles = mySystem->cycles() - 1; + uInt32 delta = cycles - myCyclesWhenTimerSet; + Int32 timer = (Int32)myTimer - (Int32)(delta >> myIntervalShift) - 1; + + // See if the timer has expired yet? + if(timer >= 0) + { + return (uInt8)timer; + } + else + { + timer = (Int32)(myTimer << myIntervalShift) - (Int32)delta - 1; + + if((timer <= -2) && !myTimerReadAfterInterrupt) + { + // Indicate that timer has been read after interrupt occured + myTimerReadAfterInterrupt = true; + myCyclesWhenInterruptReset = mySystem->cycles(); + } + + if(myTimerReadAfterInterrupt) + { + Int32 offset = myCyclesWhenInterruptReset - + (myCyclesWhenTimerSet + (myTimer << myIntervalShift)); + + timer = (Int32)myTimer - (Int32)(delta >> myIntervalShift) - offset; + } + + return (uInt8)timer; + } + } + + case 0x05: // Interrupt Flag + case 0x07: + { + uInt32 cycles = mySystem->cycles() - 1; + uInt32 delta = cycles - myCyclesWhenTimerSet; + Int32 timer = (Int32)myTimer - (Int32)(delta >> myIntervalShift) - 1; + + if((timer >= 0) || myTimerReadAfterInterrupt) + return 0x00; + else + return 0x80; + } + + default: + { +#ifdef DEBUG_ACCESSES + cerr << "BAD M6532 Peek: " << hex << addr << endl; +#endif + return 0; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6532::poke(uInt16 addr, uInt8 value) +{ + if((addr & 0x07) == 0x00) // Port A I/O Register (Joystick) + { + uInt8 a = value & myDDRA; + + myConsole.controller(Controller::Left).write(Controller::One, a & 0x10); + myConsole.controller(Controller::Left).write(Controller::Two, a & 0x20); + myConsole.controller(Controller::Left).write(Controller::Three, a & 0x40); + myConsole.controller(Controller::Left).write(Controller::Four, a & 0x80); + + myConsole.controller(Controller::Right).write(Controller::One, a & 0x01); + myConsole.controller(Controller::Right).write(Controller::Two, a & 0x02); + myConsole.controller(Controller::Right).write(Controller::Three, a & 0x04); + myConsole.controller(Controller::Right).write(Controller::Four, a & 0x08); + } + else if((addr & 0x07) == 0x01) // Port A Data Direction Register + { + myDDRA = value; + } + else if((addr & 0x07) == 0x02) // Port B I/O Register (Console switches) + { + return; + } + else if((addr & 0x07) == 0x03) // Port B Data Direction Register + { +// myDDRB = value; + return; + } + else if((addr & 0x17) == 0x14) // Write timer divide by 1 + { + myTimer = value; + myIntervalShift = 0; + myCyclesWhenTimerSet = mySystem->cycles(); + myTimerReadAfterInterrupt = false; + } + else if((addr & 0x17) == 0x15) // Write timer divide by 8 + { + myTimer = value; + myIntervalShift = 3; + myCyclesWhenTimerSet = mySystem->cycles(); + myTimerReadAfterInterrupt = false; + } + else if((addr & 0x17) == 0x16) // Write timer divide by 64 + { + myTimer = value; + myIntervalShift = 6; + myCyclesWhenTimerSet = mySystem->cycles(); + myTimerReadAfterInterrupt = false; + } + else if((addr & 0x17) == 0x17) // Write timer divide by 1024 + { + myTimer = value; + myIntervalShift = 10; + myCyclesWhenTimerSet = mySystem->cycles(); + myTimerReadAfterInterrupt = false; + } + else if((addr & 0x14) == 0x04) // Write Edge Detect Control + { +#ifdef DEBUG_ACCESSES + cerr << "M6532 Poke (Write Edge Detect): " + << ((addr & 0x02) ? "PA7 enabled" : "PA7 disabled") + << ", " + << ((addr & 0x01) ? "Positive edge" : "Negative edge") + << endl; +#endif + } + else + { +#ifdef DEBUG_ACCESSES + cerr << "BAD M6532 Poke: " << hex << addr << endl; +#endif + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6532::M6532(const M6532& c) + : myConsole(c.myConsole) +{ + assert(false); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6532& M6532::operator = (const M6532&) +{ + assert(false); + + return *this; +} + diff --git a/stella/src/emucore/M6532.hxx b/stella/src/emucore/M6532.hxx new file mode 100644 index 000000000..02c854a46 --- /dev/null +++ b/stella/src/emucore/M6532.hxx @@ -0,0 +1,129 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6532.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#ifndef M6532_HXX +#define M6532_HXX + +class Console; +class System; + +#include "bspf.hxx" +#include "Device.hxx" + +/** + RIOT + + @author Bradford W. Mott + @version $Id: M6532.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +*/ +class M6532 : public Device +{ + public: + /** + Create a new 6532 for the specified console + + @param console The console the 6532 is associated with + */ + M6532(const Console& console); + + /** + Destructor + */ + virtual ~M6532(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset cartridge to its power-on state + */ + virtual void reset(); + + /** + Notification method invoked by the system right before the + system resets its cycle counter to zero. It may be necessary + to override this method for devices that remember cycle counts. + */ + virtual void systemCyclesReset(); + + /** + Install 6532 in the specified system. Invoked by the system + when the 6532 is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + private: + // Reference to the console + const Console& myConsole; + + // An amazing 128 bytes of RAM + uInt8 myRAM[128]; + + // Current value of my Timer + uInt32 myTimer; + + // Log base 2 of the number of cycles in a timer interval + uInt32 myIntervalShift; + + // Indicates the number of cycles when the timer was last set + Int32 myCyclesWhenTimerSet; + + // Indicates when the timer was read after timer interrupt occured + Int32 myCyclesWhenInterruptReset; + + // Indicates if a read from timer has taken place after interrupt occured + bool myTimerReadAfterInterrupt; + + // Data Direction Register for Port A + uInt8 myDDRA; + + // Data Direction Register for Port B + uInt8 myDDRB; + + private: + // Copy constructor isn't supported by this class so make it private + M6532(const M6532&); + + // Assignment operator isn't supported by this class so make it private + M6532& operator = (const M6532&); +}; +#endif + diff --git a/stella/src/emucore/MD5.cxx b/stella/src/emucore/MD5.cxx new file mode 100644 index 000000000..740fd5762 --- /dev/null +++ b/stella/src/emucore/MD5.cxx @@ -0,0 +1,347 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// This file is derived from the RSA Data Security, Inc. MD5 Message-Digest +// Algorithm. See the header below for copyright information. +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: MD5.cxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#include "MD5.hxx" + +/* + Copyright (C) 1991-2, RSA Data Security, Inc. Created 1991. All + rights reserved. + + License to copy and use this software is granted provided that it + is identified as the "RSA Data Security, Inc. MD5 Message-Digest + Algorithm" in all material mentioning or referencing this software + or this function. + + License is also granted to make and use derivative works provided + that such works are identified as "derived from the RSA Data + Security, Inc. MD5 Message-Digest Algorithm" in all material + mentioning or referencing the derived work. + + RSA Data Security, Inc. makes no representations concerning either + the merchantability of this software or the suitability of this + software for any particular purpose. It is provided "as is" + without express or implied warranty of any kind. + + These notices must be retained in any copies of any part of this + documentation and/or software. +*/ + +// Setup the types used by the MD5 routines +typedef unsigned char* POINTER; +typedef uInt16 UINT2; +typedef uInt32 UINT4; + +// MD5 context. +typedef struct +{ + UINT4 state[4]; /* state (ABCD) */ + UINT4 count[2]; /* number of bits, modulo 2^64 (lsb first) */ + unsigned char buffer[64]; /* input buffer */ +} MD5_CTX; + +// Constants for MD5Transform routine. +#define S11 7 +#define S12 12 +#define S13 17 +#define S14 22 +#define S21 5 +#define S22 9 +#define S23 14 +#define S24 20 +#define S31 4 +#define S32 11 +#define S33 16 +#define S34 23 +#define S41 6 +#define S42 10 +#define S43 15 +#define S44 21 + +static void MD5Init(MD5_CTX*); +static void MD5Update(MD5_CTX*, const unsigned char*, unsigned int); +static void MD5Final(unsigned char[16], MD5_CTX*); +static void MD5Transform(UINT4 [4], const unsigned char [64]); +static void Encode(unsigned char*, UINT4*, unsigned int); +static void Decode(UINT4*, const unsigned char*, unsigned int); +static void MD5_memcpy(POINTER, POINTER, unsigned int); +static void MD5_memset(POINTER, int, unsigned int); + +static unsigned char PADDING[64] = { + 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// F, G, H and I are basic MD5 functions. +#define F(x, y, z) (((x) & (y)) | ((~x) & (z))) +#define G(x, y, z) (((x) & (z)) | ((y) & (~z))) +#define H(x, y, z) ((x) ^ (y) ^ (z)) +#define I(x, y, z) ((y) ^ ((x) | (~z))) + +// ROTATE_LEFT rotates x left n bits. +#define ROTATE_LEFT(x, n) (((x) << (n)) | ((x) >> (32-(n)))) + +// FF, GG, HH, and II transformations for rounds 1, 2, 3, and 4. +// Rotation is separate from addition to prevent recomputation. +#define FF(a, b, c, d, x, s, ac) { \ + (a) += F ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define GG(a, b, c, d, x, s, ac) { \ + (a) += G ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define HH(a, b, c, d, x, s, ac) { \ + (a) += H ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } +#define II(a, b, c, d, x, s, ac) { \ + (a) += I ((b), (c), (d)) + (x) + (UINT4)(ac); \ + (a) = ROTATE_LEFT ((a), (s)); \ + (a) += (b); \ + } + +// MD5 initialization. Begins an MD5 operation, writing a new context. +static void MD5Init(MD5_CTX* context) +{ + context->count[0] = context->count[1] = 0; + /* Load magic initialization constants. */ + context->state[0] = 0x67452301; + context->state[1] = 0xefcdab89; + context->state[2] = 0x98badcfe; + context->state[3] = 0x10325476; +} + +// MD5 block update operation. Continues an MD5 message-digest +// operation, processing another message block, and updating the +// context. +static void MD5Update(MD5_CTX* context, const unsigned char* input, + unsigned int inputLen) +{ + unsigned int i, index, partLen; + + /* Compute number of bytes mod 64 */ + index = (unsigned int)((context->count[0] >> 3) & 0x3F); + + /* Update number of bits */ + if ((context->count[0] += ((UINT4)inputLen << 3)) + < ((UINT4)inputLen << 3)) + context->count[1]++; + context->count[1] += ((UINT4)inputLen >> 29); + + partLen = 64 - index; + + /* Transform as many times as possible. */ + if (inputLen >= partLen) { + MD5_memcpy ((POINTER)&context->buffer[index], (POINTER)input, partLen); + MD5Transform (context->state, context->buffer); + + for (i = partLen; i + 63 < inputLen; i += 64) + MD5Transform (context->state, &input[i]); + + index = 0; + } + else + i = 0; + + /* Buffer remaining input */ + MD5_memcpy((POINTER)&context->buffer[index], (POINTER)&input[i], inputLen-i); +} + +// MD5 finalization. Ends an MD5 message-digest operation, writing the +// the message digest and zeroizing the context. +static void MD5Final(unsigned char digest[16], MD5_CTX* context) +{ + unsigned char bits[8]; + unsigned int index, padLen; + + /* Save number of bits */ + Encode (bits, context->count, 8); + + /* Pad out to 56 mod 64. */ + index = (unsigned int)((context->count[0] >> 3) & 0x3f); + padLen = (index < 56) ? (56 - index) : (120 - index); + MD5Update (context, PADDING, padLen); + + /* Append length (before padding) */ + MD5Update (context, bits, 8); + /* Store state in digest */ + Encode (digest, context->state, 16); + + /* Zeroize sensitive information. */ + MD5_memset ((POINTER)context, 0, sizeof (*context)); +} + +// MD5 basic transformation. Transforms state based on block. +static void MD5Transform(UINT4 state[4], const unsigned char block[64]) +{ + UINT4 a = state[0], b = state[1], c = state[2], d = state[3], x[16]; + + Decode (x, block, 64); + + /* Round 1 */ + FF (a, b, c, d, x[ 0], S11, 0xd76aa478); /* 1 */ + FF (d, a, b, c, x[ 1], S12, 0xe8c7b756); /* 2 */ + FF (c, d, a, b, x[ 2], S13, 0x242070db); /* 3 */ + FF (b, c, d, a, x[ 3], S14, 0xc1bdceee); /* 4 */ + FF (a, b, c, d, x[ 4], S11, 0xf57c0faf); /* 5 */ + FF (d, a, b, c, x[ 5], S12, 0x4787c62a); /* 6 */ + FF (c, d, a, b, x[ 6], S13, 0xa8304613); /* 7 */ + FF (b, c, d, a, x[ 7], S14, 0xfd469501); /* 8 */ + FF (a, b, c, d, x[ 8], S11, 0x698098d8); /* 9 */ + FF (d, a, b, c, x[ 9], S12, 0x8b44f7af); /* 10 */ + FF (c, d, a, b, x[10], S13, 0xffff5bb1); /* 11 */ + FF (b, c, d, a, x[11], S14, 0x895cd7be); /* 12 */ + FF (a, b, c, d, x[12], S11, 0x6b901122); /* 13 */ + FF (d, a, b, c, x[13], S12, 0xfd987193); /* 14 */ + FF (c, d, a, b, x[14], S13, 0xa679438e); /* 15 */ + FF (b, c, d, a, x[15], S14, 0x49b40821); /* 16 */ + + /* Round 2 */ + GG (a, b, c, d, x[ 1], S21, 0xf61e2562); /* 17 */ + GG (d, a, b, c, x[ 6], S22, 0xc040b340); /* 18 */ + GG (c, d, a, b, x[11], S23, 0x265e5a51); /* 19 */ + GG (b, c, d, a, x[ 0], S24, 0xe9b6c7aa); /* 20 */ + GG (a, b, c, d, x[ 5], S21, 0xd62f105d); /* 21 */ + GG (d, a, b, c, x[10], S22, 0x2441453); /* 22 */ + GG (c, d, a, b, x[15], S23, 0xd8a1e681); /* 23 */ + GG (b, c, d, a, x[ 4], S24, 0xe7d3fbc8); /* 24 */ + GG (a, b, c, d, x[ 9], S21, 0x21e1cde6); /* 25 */ + GG (d, a, b, c, x[14], S22, 0xc33707d6); /* 26 */ + GG (c, d, a, b, x[ 3], S23, 0xf4d50d87); /* 27 */ + GG (b, c, d, a, x[ 8], S24, 0x455a14ed); /* 28 */ + GG (a, b, c, d, x[13], S21, 0xa9e3e905); /* 29 */ + GG (d, a, b, c, x[ 2], S22, 0xfcefa3f8); /* 30 */ + GG (c, d, a, b, x[ 7], S23, 0x676f02d9); /* 31 */ + GG (b, c, d, a, x[12], S24, 0x8d2a4c8a); /* 32 */ + + /* Round 3 */ + HH (a, b, c, d, x[ 5], S31, 0xfffa3942); /* 33 */ + HH (d, a, b, c, x[ 8], S32, 0x8771f681); /* 34 */ + HH (c, d, a, b, x[11], S33, 0x6d9d6122); /* 35 */ + HH (b, c, d, a, x[14], S34, 0xfde5380c); /* 36 */ + HH (a, b, c, d, x[ 1], S31, 0xa4beea44); /* 37 */ + HH (d, a, b, c, x[ 4], S32, 0x4bdecfa9); /* 38 */ + HH (c, d, a, b, x[ 7], S33, 0xf6bb4b60); /* 39 */ + HH (b, c, d, a, x[10], S34, 0xbebfbc70); /* 40 */ + HH (a, b, c, d, x[13], S31, 0x289b7ec6); /* 41 */ + HH (d, a, b, c, x[ 0], S32, 0xeaa127fa); /* 42 */ + HH (c, d, a, b, x[ 3], S33, 0xd4ef3085); /* 43 */ + HH (b, c, d, a, x[ 6], S34, 0x4881d05); /* 44 */ + HH (a, b, c, d, x[ 9], S31, 0xd9d4d039); /* 45 */ + HH (d, a, b, c, x[12], S32, 0xe6db99e5); /* 46 */ + HH (c, d, a, b, x[15], S33, 0x1fa27cf8); /* 47 */ + HH (b, c, d, a, x[ 2], S34, 0xc4ac5665); /* 48 */ + + /* Round 4 */ + II (a, b, c, d, x[ 0], S41, 0xf4292244); /* 49 */ + II (d, a, b, c, x[ 7], S42, 0x432aff97); /* 50 */ + II (c, d, a, b, x[14], S43, 0xab9423a7); /* 51 */ + II (b, c, d, a, x[ 5], S44, 0xfc93a039); /* 52 */ + II (a, b, c, d, x[12], S41, 0x655b59c3); /* 53 */ + II (d, a, b, c, x[ 3], S42, 0x8f0ccc92); /* 54 */ + II (c, d, a, b, x[10], S43, 0xffeff47d); /* 55 */ + II (b, c, d, a, x[ 1], S44, 0x85845dd1); /* 56 */ + II (a, b, c, d, x[ 8], S41, 0x6fa87e4f); /* 57 */ + II (d, a, b, c, x[15], S42, 0xfe2ce6e0); /* 58 */ + II (c, d, a, b, x[ 6], S43, 0xa3014314); /* 59 */ + II (b, c, d, a, x[13], S44, 0x4e0811a1); /* 60 */ + II (a, b, c, d, x[ 4], S41, 0xf7537e82); /* 61 */ + II (d, a, b, c, x[11], S42, 0xbd3af235); /* 62 */ + II (c, d, a, b, x[ 2], S43, 0x2ad7d2bb); /* 63 */ + II (b, c, d, a, x[ 9], S44, 0xeb86d391); /* 64 */ + + state[0] += a; + state[1] += b; + state[2] += c; + state[3] += d; + + /* Zeroize sensitive information. */ + MD5_memset ((POINTER)x, 0, sizeof (x)); +} + +// Encodes input (UINT4) into output (unsigned char). Assumes len is +// a multiple of 4. +static void Encode(unsigned char* output, UINT4* input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) { + output[j] = (unsigned char)(input[i] & 0xff); + output[j+1] = (unsigned char)((input[i] >> 8) & 0xff); + output[j+2] = (unsigned char)((input[i] >> 16) & 0xff); + output[j+3] = (unsigned char)((input[i] >> 24) & 0xff); + } +} + +// Decodes input (unsigned char) into output (UINT4). Assumes len is +// a multiple of 4. +static void Decode(UINT4* output, const unsigned char* input, unsigned int len) +{ + unsigned int i, j; + + for (i = 0, j = 0; j < len; i++, j += 4) + output[i] = ((UINT4)input[j]) | (((UINT4)input[j+1]) << 8) | + (((UINT4)input[j+2]) << 16) | (((UINT4)input[j+3]) << 24); +} + +// Note: Replace "for loop" with standard memcpy if possible. +static void MD5_memcpy(POINTER output, POINTER input, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + output[i] = input[i]; +} + +// Note: Replace "for loop" with standard memset if possible. +static void MD5_memset(POINTER output, int value, unsigned int len) +{ + unsigned int i; + + for (i = 0; i < len; i++) + ((char *)output)[i] = (char)value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string MD5(const uInt8* buffer, uInt32 length) +{ + char hex[] = "0123456789abcdef"; + MD5_CTX context; + unsigned char md5[16]; + + MD5Init(&context); + MD5Update(&context, buffer, length); + MD5Final(md5, &context); + + string result; + for(int t = 0; t < 16; ++t) + { + result += hex[(md5[t] >> 4) & 0x0f]; + result += hex[md5[t] & 0x0f]; + } + + return result; +} + diff --git a/stella/src/emucore/MD5.hxx b/stella/src/emucore/MD5.hxx new file mode 100644 index 000000000..0e45a405c --- /dev/null +++ b/stella/src/emucore/MD5.hxx @@ -0,0 +1,35 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: MD5.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#ifndef MD5_HXX +#define MD5_HXX + +#include "bspf.hxx" + +/** + Get the MD5 Message-Digest of the specified message with the + given length. The digest consists of 32 hexadecimal digits. + + @param buffer The message to compute the digest of + @param length The length of the message + @return The message-digest +*/ +string MD5(const uInt8* buffer, uInt32 length); + +#endif + diff --git a/stella/src/emucore/MediaSrc.cxx b/stella/src/emucore/MediaSrc.cxx new file mode 100644 index 000000000..2ca5840cf --- /dev/null +++ b/stella/src/emucore/MediaSrc.cxx @@ -0,0 +1,44 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: MediaSrc.cxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#include +#include "MediaSrc.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +MediaSource::MediaSource() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +MediaSource::~MediaSource() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +MediaSource::MediaSource(const MediaSource&) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +MediaSource& MediaSource::operator = (const MediaSource&) +{ + assert(false); + + return *this; +} + diff --git a/stella/src/emucore/MediaSrc.hxx b/stella/src/emucore/MediaSrc.hxx new file mode 100644 index 000000000..660970bf1 --- /dev/null +++ b/stella/src/emucore/MediaSrc.hxx @@ -0,0 +1,105 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: MediaSrc.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#ifndef MEDIASOURCE_HXX +#define MEDIASOURCE_HXX + +class MediaSource; + +#include "bspf.hxx" + +/** + This class provides an interface for accessing graphics data. + + @author Bradford W. Mott + @version $Id: MediaSrc.hxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +*/ +class MediaSource +{ + public: + /** + Create a new media source + */ + MediaSource(); + + /** + Destructor + */ + virtual ~MediaSource(); + + public: + /** + This method should be called at an interval corresponding to + the desired frame rate to update the media source. + */ + virtual void update() = 0; + + /** + Answers the current frame buffer + + @return Pointer to the current frame buffer + */ + virtual uInt8* currentFrameBuffer() const = 0; + + /** + Answers the previous frame buffer + + @return Pointer to the previous frame buffer + */ + virtual uInt8* previousFrameBuffer() const = 0; + + public: + /** + Get the palette which maps frame data to RGB values. + + @return Array of integers which represent the palette (RGB) + */ + virtual const uInt32* palette() const = 0; + + /** + Answers the height of the frame buffer + + @return The frame's height + */ + virtual uInt32 height() const = 0; + + /** + Answers the width of the frame buffer + + @return The frame's width + */ + virtual uInt32 width() const = 0; + + public: + /** + Answers the total number of scanlines the media source generated + in producing the current frame buffer. + + @return The total number of scanlines generated + */ + virtual uInt32 scanlines() const = 0; + + private: + // Copy constructor isn't supported by this class so make it private + MediaSource(const MediaSource&); + + // Assignment operator isn't supported by this class so make it private + MediaSource& operator = (const MediaSource&); +}; +#endif + diff --git a/stella/src/emucore/Paddles.cxx b/stella/src/emucore/Paddles.cxx new file mode 100644 index 000000000..ef2ee27e0 --- /dev/null +++ b/stella/src/emucore/Paddles.cxx @@ -0,0 +1,76 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Paddles.cxx,v 1.1.1.1 2001-12-27 19:54:22 bwmott Exp $ +//============================================================================ + +#include +#include "Event.hxx" +#include "Paddles.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Paddles::Paddles(Jack jack, const Event& event) + : Controller(jack, event) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Paddles::~Paddles() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Paddles::read(DigitalPin pin) +{ + switch(pin) + { + case Three: + return (myJack == Left) ? (myEvent.get(Event::PaddleOneFire) == 0) : + (myEvent.get(Event::PaddleThreeFire) == 0); + + case Four: + return (myJack == Left) ? (myEvent.get(Event::PaddleZeroFire) == 0) : + (myEvent.get(Event::PaddleTwoFire) == 0); + + default: + // Other pins are not connected (floating high) + return true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int32 Paddles::read(AnalogPin pin) +{ + switch(pin) + { + case Five: + return (myJack == Left) ? myEvent.get(Event::PaddleOneResistance) : + myEvent.get(Event::PaddleThreeResistance); + + case Nine: + return (myJack == Left) ? myEvent.get(Event::PaddleZeroResistance) : + myEvent.get(Event::PaddleTwoResistance); + + default: + return maximumResistance; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Paddles::write(DigitalPin, bool) +{ + // Writing doesn't do anything to the paddles... +} + diff --git a/stella/src/emucore/Paddles.hxx b/stella/src/emucore/Paddles.hxx new file mode 100644 index 000000000..bdb7cbeec --- /dev/null +++ b/stella/src/emucore/Paddles.hxx @@ -0,0 +1,76 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Paddles.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#ifndef PADDLES_HXX +#define PADDLES_HXX + +#include "bspf.hxx" +#include "Control.hxx" + +/** + The standard Atari 2600 pair of paddle controllers. + + @author Bradford W. Mott + @version $Id: Paddles.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +*/ +class Paddles : public Controller +{ + public: + /** + Create a new pair of paddle controllers plugged into the specified jack + + @param jack The jack the controller is plugged into + @param event The event object to use for events + */ + Paddles(Jack jack, const Event& event); + + /** + Destructor + */ + virtual ~Paddles(); + + public: + /** + Read the value of the specified digital pin for this controller. + + @param pin The pin of the controller jack to read + @return The state of the pin + */ + virtual bool read(DigitalPin pin); + + /** + Read the resistance at the specified analog pin for this controller. + The returned value is the resistance measured in ohms. + + @param pin The pin of the controller jack to read + @return The resistance at the specified pin + */ + virtual Int32 read(AnalogPin pin); + + /** + Write the given value to the specified digital pin for this + controller. Writing is only allowed to the pins associated + with the PIA. Therefore you cannot write to pin six. + + @param pin The pin of the controller jack to write to + @param value The value to write to the pin + */ + virtual void write(DigitalPin pin, bool value); +}; +#endif + diff --git a/stella/src/emucore/Props.cxx b/stella/src/emucore/Props.cxx new file mode 100644 index 000000000..bec5bb838 --- /dev/null +++ b/stella/src/emucore/Props.cxx @@ -0,0 +1,258 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Props.cxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#include "Props.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Properties::Properties(const Properties* defaults) +{ + myDefaults = defaults; + myCapacity = 16; + myProperties = new Property[myCapacity]; + mySize = 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Properties::Properties(const Properties& properties) +{ + copy(properties); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Properties::~Properties() +{ + // Free the properties array + delete[] myProperties; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Properties::get(const string& key) const +{ + // Try to find the named property and answer its value + for(uInt32 i = 0; i < mySize; ++i) + { + if(key == myProperties[i].key) + { + return myProperties[i].value; + } + } + + // Oops, property wasn't found so ask defaults if we have one + if(myDefaults != 0) + { + // Ask the default properties object to find the key + return myDefaults->get(key); + } + else + { + // No default properties object so just return the empty string + return ""; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Properties::set(const string& key, const string& value) +{ + // See if the property already exists + for(uInt32 i = 0; i < mySize; ++i) + { + if(key == myProperties[i].key) + { + myProperties[i].value = value; + return; + } + } + + // See if the array needs to be resized + if(mySize == myCapacity) + { + // Yes, so we'll make the array twice as large + Property* newProperties = new Property[myCapacity * 2]; + + for(uInt32 i = 0; i < mySize; ++i) + { + newProperties[i] = myProperties[i]; + } + + delete[] myProperties; + + myProperties = newProperties; + myCapacity *= 2; + } + + // Add new property to the array + myProperties[mySize].key = key; + myProperties[mySize].value = value; + + ++mySize; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Properties::load(istream& in) +{ + // Empty my property array + mySize = 0; + + // Loop reading properties + for(;;) + { + // Get the key associated with this property + string key = readQuotedString(in); + + // Make sure the stream is still okay + if(!in) + { + return; + } + + // A null key signifies the end of the property list + if(key == "") + { + break; + } + + // Get the value associated with this property + string value = readQuotedString(in); + + // Make sure the stream is still okay + if(!in) + { + return; + } + + // Set the property + set(key, value); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Properties::save(ostream& out) +{ + // Write out each of the key and value pairs + for(uInt32 i = 0; i < mySize; ++i) + { + writeQuotedString(out, myProperties[i].key); + out.put(' '); + writeQuotedString(out, myProperties[i].value); + out.put('\n'); + } + + // Put a trailing null string so we know when to stop reading + writeQuotedString(out, ""); + out.put('\n'); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +string Properties::readQuotedString(istream& in) +{ + char c; + + // Read characters until we see a quote + while(in.get(c)) + { + if(c == '"') + { + break; + } + } + + // Read characters until we see the close quote + string s; + while(in.get(c)) + { + if((c == '\\') && (in.peek() == '"')) + { + in.get(c); + } + else if((c == '\\') && (in.peek() == '\\')) + { + in.get(c); + } + else if(c == '"') + { + break; + } + else if(c == '\r') + { + continue; + } + + s += c; + } + + return s; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Properties::writeQuotedString(ostream& out, const string& s) +{ + out.put('"'); + for(uInt32 i = 0; i < s.length(); ++i) + { + if(s[i] == '\\') + { + out.put('\\'); + out.put('\\'); + } + else if(s[i] == '\"') + { + out.put('\\'); + out.put('"'); + } + else + { + out.put(s[i]); + } + } + out.put('"'); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Properties& Properties::operator = (const Properties& properties) +{ + // Do the assignment only if this isn't a self assignment + if(this != &properties) + { + // Free the properties array + delete[] myProperties; + + // Now, make myself a copy of the given object + copy(properties); + } + + return *this; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Properties::copy(const Properties& properties) +{ + // Remember the defaults to use + myDefaults = properties.myDefaults; + + // Create an array of the same size as properties + myCapacity = properties.myCapacity; + myProperties = new Property[myCapacity]; + + // Now, copy each property from properties + mySize = properties.mySize; + for(uInt32 i = 0; i < mySize; ++i) + { + myProperties[i] = properties.myProperties[i]; + } +} + diff --git a/stella/src/emucore/Props.hxx b/stella/src/emucore/Props.hxx new file mode 100644 index 000000000..ff93f17dc --- /dev/null +++ b/stella/src/emucore/Props.hxx @@ -0,0 +1,150 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Props.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#ifndef PROPERTIES_HXX +#define PROPERTIES_HXX + +#include "bspf.hxx" + +/** + This class represents objects which maintain a collection of + properties. A property is a key and its corresponding value. + + A properties object can contain a reference to another properties + object as its "defaults"; this second properties object is searched + if the property key is not found in the original property list. + + @author Bradford W. Mott + @version $Id: Props.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +*/ +class Properties +{ + public: + /** + Creates an empty properties object with the specified defaults. The + new properties object does not claim ownership of the defaults. + + @param defaults The defaults + */ + Properties(const Properties* defaults = 0); + + /** + Creates a properties list by copying another one + + @param properties The properties to copy + */ + Properties(const Properties& properties); + + /** + Destructor + */ + virtual ~Properties(); + + public: + /** + Get the value assigned to the specified key. If the key does + not exist then the empty string is returned. + + @param key The key of the property to lookup + @return The value of the property + */ + string get(const string& key) const; + + /** + Set the value associated with key to the given value. + + @param key The key of the property to set + @param value The value to assign to the property + */ + void set(const string& key, const string& value); + + public: + /** + Load properties from the specified input stream + + @param in The input stream to use + */ + void load(istream& in); + + /** + Save properties to the specified output stream + + @param out The output stream to use + */ + void save(ostream& out); + + public: + /** + Read the next quoted string from the specified input stream + and returns it. + + @param in The input stream to use + @return The string inside the quotes + */ + static string readQuotedString(istream& in); + + /** + Write the specified string to the given output stream as a + quoted string. + + @param out The output stream to use + @param s The string to output + */ + static void writeQuotedString(ostream& out, const string& s); + + public: + /** + Overloaded assignment operator + + @param properties The properties object to set myself equal to + @return Myself after assignment has taken place + */ + Properties& operator = (const Properties& properties); + + private: + /** + Helper function to perform a deep copy of the specified + properties. Assumes that old properties have already been + freed. + + @param properties The properties object to copy myself from + */ + void copy(const Properties& properties); + + private: + // Structure used for storing properties + struct Property + { + string key; + string value; + }; + + // Pointer to properties object to use for defaults or the null pointer + const Properties* myDefaults; + + // Pointer to a dynamically allocated array of properties + Property* myProperties; + + // Current capacity of the properties array + unsigned int myCapacity; + + // Size of the properties array (i.e. the number of pairs) + unsigned int mySize; +}; +#endif + diff --git a/stella/src/emucore/PropsSet.cxx b/stella/src/emucore/PropsSet.cxx new file mode 100644 index 000000000..25d59d79b --- /dev/null +++ b/stella/src/emucore/PropsSet.cxx @@ -0,0 +1,206 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: PropsSet.cxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#include +#include "Props.hxx" +#include "PropsSet.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PropertiesSet::PropertiesSet(const string& key) + : myKey(key) +{ + myCapacity = 16; + myProperties = new Properties[myCapacity]; + mySize = 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PropertiesSet::PropertiesSet(const PropertiesSet& p) + : myKey(p.myKey) +{ + myCapacity = p.myCapacity; + myProperties = new Properties[myCapacity]; + mySize = p.mySize; + + // Copy the properties from the other set + for(uInt32 i = 0; i < mySize; ++i) + { + myProperties[i] = p.myProperties[i]; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PropertiesSet::~PropertiesSet() +{ + delete[] myProperties; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Properties& PropertiesSet::get(uInt32 i) +{ + // Make sure index is within range + assert(i < mySize); + + return myProperties[i]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PropertiesSet::insert(const Properties& properties) +{ + uInt32 i; + uInt32 j; + + // Get the key of the properties + string name = properties.get(myKey); + + // See if the key already exists (we could use a binary search here...) + for(i = 0; i < mySize; ++i) + { + if(name == myProperties[i].get(myKey)) + { + // Copy the properties which are being inserted + myProperties[i] = properties; + return; + } + } + + // See if the properties array needs to be resized + if(mySize == myCapacity) + { + Properties* newProperties = new Properties[myCapacity *= 2]; + + for(i = 0; i < mySize; ++i) + { + newProperties[i] = myProperties[i]; + } + + delete[] myProperties; + + myProperties = newProperties; + } + + // Find the correct place to insert the properties at + for(i = 0; (i < mySize) && (myProperties[i].get(myKey) < name); ++i); + + // Okay, make room for the properties + for(j = mySize; j > i; --j) + { + myProperties[j] = myProperties[j - 1]; + } + + // Now, put the properties in the array + myProperties[i] = properties; + + ++mySize; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 PropertiesSet::size() const +{ + return mySize; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PropertiesSet::erase(uInt32 i) +{ + // Make sure index is within range + assert(i < mySize); + + for(uInt32 j = i + 1; j < mySize; ++j) + { + myProperties[j - 1] = myProperties[j]; + } + + --mySize; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PropertiesSet::load(istream& in, const Properties* defaults) +{ + // Empty my properties array + mySize = 0; + + // Loop reading properties + for(;;) + { + // Read char's until we see a quote as the next char or EOF is reached + while(in && (in.peek() != '"')) + { + char c; + in.get(c); + + // If we see the comment character then ignore the line + if(c == ';') + { + while(in && (c != '\n')) + { + in.get(c); + } + } + } + + // Make sure the stream is still good or we're done + if(!in) + { + break; + } + + // Get the property list associated with this profile + Properties properties(defaults); + properties.load(in); + + // If the stream is still good then insert the properties + if(in) + { + insert(properties); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PropertiesSet::save(ostream& out) +{ + // Write each of the properties out + for(uInt32 i = 0; i < mySize; ++i) + { + myProperties[i].save(out); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PropertiesSet& PropertiesSet::operator = (const PropertiesSet& p) +{ + if(this != &p) + { + delete[] myProperties; + + myKey = p.myKey; + myCapacity = p.myCapacity; + myProperties = new Properties[myCapacity]; + mySize = p.mySize; + + // Copy the properties from the other set + for(uInt32 i = 0; i < mySize; ++i) + { + myProperties[i] = p.myProperties[i]; + } + } + + return *this; +} + diff --git a/stella/src/emucore/PropsSet.hxx b/stella/src/emucore/PropsSet.hxx new file mode 100644 index 000000000..b990d5862 --- /dev/null +++ b/stella/src/emucore/PropsSet.hxx @@ -0,0 +1,128 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: PropsSet.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#ifndef PROPERTIESSET_HXX +#define PROPERTIESSET_HXX + +class Properties; + +#include "bspf.hxx" +#include "Props.hxx" + +/** + This class maintains a sorted collection of properties. Upon + construction one property is distinguished as the key for sorting. + + @author Bradford W. Mott + @version $Id: PropsSet.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +*/ +class PropertiesSet +{ + public: + /** + Create an empty properties set object using the specified + property as the key for sorting. + + @param key The property to use as the key + */ + PropertiesSet(const string& key); + + /** + Create a properties set object by copying another one + + @param set The properties set to copy + */ + PropertiesSet(const PropertiesSet& set); + + /** + Destructor + */ + virtual ~PropertiesSet(); + + public: + /** + Get the i'th properties from the set + + @param i The index of the properties to get + @return The properties stored at the i'th location + */ + const Properties& get(uInt32 i); + + /** + Insert the properties into the set. If a duplicate is inserted + the old properties are overwritten with the new ones. + + @param properties The collection of properties + */ + void insert(const Properties& properties); + + /** + Get the number of properties in the collection. + + @return The number of properties in the collection + */ + uInt32 size() const; + + /** + Erase the i'th properties from the collection. + + @param i The profile index + */ + void erase(uInt32 i); + + public: + /** + Load properties from the specified input stream. Use the given + defaults properties as the defaults for any properties loaded. + + @param in The input stream to use + @param defaults The default properties to use + */ + void load(istream& in, const Properties* defaults); + + /** + Save properties to the specified output stream + + @param out The output stream to use + */ + void save(ostream& out); + + public: + /** + Overloaded assignment operator + + @param propertiesSet The properties set to set myself equal to + @return Myself after assignment has taken place + */ + PropertiesSet& operator = (const PropertiesSet& propertiesSet); + + private: + // Property to use as the key + string myKey; + + // Pointer to a dynamically allocated array of properties + Properties* myProperties; + + // Current capacity of the properties array + unsigned int myCapacity; + + // The size of the properties array (i.e. the number of properties in it) + unsigned int mySize; +}; +#endif + diff --git a/stella/src/emucore/Random.cxx b/stella/src/emucore/Random.cxx new file mode 100644 index 000000000..e0d61cfd4 --- /dev/null +++ b/stella/src/emucore/Random.cxx @@ -0,0 +1,53 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Random.cxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#include +#include "Random.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Random::seed(uInt32 value) +{ + ourSeed = value; + ourSeeded = true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Random::Random() +{ + // If we haven't been seeded then seed ourself + if(!ourSeeded) + { + ourSeed = (uInt32)time(0); + ourSeeded = true; + } + + myValue = ourSeed; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 Random::next() +{ + return (myValue = (myValue * 2416 + 374441) % 1771875); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 Random::ourSeed = 0; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool Random::ourSeeded = false; + diff --git a/stella/src/emucore/Random.hxx b/stella/src/emucore/Random.hxx new file mode 100644 index 000000000..c1af19b2c --- /dev/null +++ b/stella/src/emucore/Random.hxx @@ -0,0 +1,69 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Random.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#ifndef RANDOM_HXX +#define RANDOM_HXX + +#include "bspf.hxx" + +/** + This is a quick-and-dirty random number generator. It is based on + information in Chapter 7 of "Numerical Recipes in C". It's a simple + linear congruential generator. + + @author Bradford W. Mott + @version $Id: Random.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +*/ +class Random +{ + public: + /** + Class method which allows you to set the seed that'll be used + for created new instances of this class + + @param value The value to seed the random number generator with + */ + static void seed(uInt32 value); + + public: + /** + Create a new random number generator + */ + Random(); + + public: + /** + Answer the next random number from the random number generator + + @return A random number + */ + uInt32 next(); + + private: + // Indicates the next random number + uInt32 myValue; + + private: + // Seed to use for creating new random number generators + static uInt32 ourSeed; + + // Indicates if seed has been set or not + static bool ourSeeded; +}; +#endif + diff --git a/stella/src/emucore/Sound.cxx b/stella/src/emucore/Sound.cxx new file mode 100644 index 000000000..782b8b6b9 --- /dev/null +++ b/stella/src/emucore/Sound.cxx @@ -0,0 +1,44 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Sound.cxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#include "Sound.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Sound::Sound() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Sound::~Sound() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Sound::set(Sound::Register, uInt8) +{ + // This sound class doesn't do anything when a register is set + // since we're not handling sound +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Sound::mute(bool) +{ + // There's nothing for us to do when the sound is muted since + // we're not handling sound +} + diff --git a/stella/src/emucore/Sound.hxx b/stella/src/emucore/Sound.hxx new file mode 100644 index 000000000..7a8c0d5b8 --- /dev/null +++ b/stella/src/emucore/Sound.hxx @@ -0,0 +1,71 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Sound.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#ifndef SOUND_HXX +#define SOUND_HXX + +#include "bspf.hxx" + +/** + Base class that defines the standard API for sound classes. You + should derive a new class from this one to create a new sound system + for a specific operating system. + + @author Bradford W. Mott + @version $Id: Sound.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +*/ +class Sound +{ + public: + /** + Enumeration of the TIA sound registers + */ + enum Register + { + AUDF0, AUDF1, AUDC0, AUDC1, AUDV0, AUDV1 + }; + + public: + /** + Create a new sound object + */ + Sound(); + + /** + Destructor + */ + virtual ~Sound(); + + public: + /** + Set the value of the specified sound register + + @param reg The sound register to set + @param val The new value for the sound register + */ + virtual void set(Sound::Register reg, uInt8 val); + + /** + Set the mute state of the sound object + + @param state Mutes sound iff true + */ + virtual void mute(bool state); +}; +#endif + diff --git a/stella/src/emucore/Switches.cxx b/stella/src/emucore/Switches.cxx new file mode 100644 index 000000000..d56865723 --- /dev/null +++ b/stella/src/emucore/Switches.cxx @@ -0,0 +1,111 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Switches.cxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#include "Event.hxx" +#include "Props.hxx" +#include "Switches.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Switches::Switches(const Event& event, const Properties& properties) + : myEvent(event), + mySwitches(0xFF) +{ + if(properties.get("Console.RightDifficulty") == "B") + { + mySwitches &= ~0x80; + } + else + { + mySwitches |= 0x80; + } + + if(properties.get("Console.LeftDifficulty") == "B") + { + mySwitches &= ~0x40; + } + else + { + mySwitches |= 0x40; + } + + if(properties.get("Console.TelevisionType") == "Color") + { + mySwitches |= 0x08; + } + else + { + mySwitches &= ~0x08; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Switches::~Switches() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 Switches::read() +{ + if(myEvent.get(Event::ConsoleColor) != 0) + { + mySwitches |= 0x08; + } + else if(myEvent.get(Event::ConsoleBlackWhite) != 0) + { + mySwitches &= ~0x08; + } + + if(myEvent.get(Event::ConsoleRightDifficultyA) != 0) + { + mySwitches &= ~0x80; + } + else if(myEvent.get(Event::ConsoleRightDifficultyB) != 0) + { + mySwitches |= 0x80; + } + + if(myEvent.get(Event::ConsoleLeftDifficultyA) != 0) + { + mySwitches &= ~0x40; + } + else if(myEvent.get(Event::ConsoleLeftDifficultyB) != 0) + { + mySwitches |= 0x40; + } + + if(myEvent.get(Event::ConsoleSelect) != 0) + { + mySwitches &= ~0x02; + } + else + { + mySwitches |= 0x02; + } + + if(myEvent.get(Event::ConsoleReset) != 0) + { + mySwitches &= ~0x01; + } + else + { + mySwitches |= 0x01; + } + + return mySwitches; +} + diff --git a/stella/src/emucore/Switches.hxx b/stella/src/emucore/Switches.hxx new file mode 100644 index 000000000..e58361f75 --- /dev/null +++ b/stella/src/emucore/Switches.hxx @@ -0,0 +1,66 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Switches.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +//============================================================================ + +#ifndef SWITCHES_HXX +#define SWITCHES_HXX + +class Event; +class Properties; +class Switches; + +#include "bspf.hxx" + +/** + This class represents the console switches of the game console. + + @author Bradford W. Mott + @version $Id: Switches.hxx,v 1.1.1.1 2001-12-27 19:54:23 bwmott Exp $ +*/ +class Switches +{ + public: + /** + Create a new set of switches using the specified events and + properties + + @param event The event object to use for events + */ + Switches(const Event& event, const Properties& properties); + + /** + Destructor + */ + virtual ~Switches(); + + public: + /** + Get the value of the console switches + + @return The 8 bits which represent the state of the console switches + */ + uInt8 read(); + + private: + // Reference to the event object to use + const Event& myEvent; + + // State of the console switches + uInt8 mySwitches; +}; +#endif + diff --git a/stella/src/emucore/TIA.cxx b/stella/src/emucore/TIA.cxx new file mode 100644 index 000000000..51fbab642 --- /dev/null +++ b/stella/src/emucore/TIA.cxx @@ -0,0 +1,2698 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: TIA.cxx,v 1.1.1.1 2001-12-27 19:54:25 bwmott Exp $ +//============================================================================ + +#include +#include +#include + +#include "Console.hxx" +#include "Control.hxx" +#include "M6502.hxx" +#include "Sound.hxx" +#include "System.hxx" +#include "TIA.hxx" + +#define HBLANK 68 + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +TIA::TIA(const Console& console, Sound& sound) + : myConsole(console), + mySound(sound), + myCOLUBK(myColor[0]), + myCOLUPF(myColor[1]), + myCOLUP0(myColor[2]), + myCOLUP1(myColor[3]) +{ + // Allocate buffers for two frame buffers + myCurrentFrameBuffer = new uInt8[160 * 300]; + myPreviousFrameBuffer = new uInt8[160 * 300]; + + for(uInt16 x = 0; x < 2; ++x) + { + for(uInt16 enabled = 0; enabled < 256; ++enabled) + { + if(enabled & PriorityBit) + { + uInt8 color = 0; + + if((enabled & (myP1Bit | myM1Bit)) != 0) + color = 3; + if((enabled & (myP0Bit | myM0Bit)) != 0) + color = 2; + if((enabled & myBLBit) != 0) + color = 1; + if((enabled & myPFBit) != 0) + color = (enabled & ScoreBit) ? ((x == 0) ? 2 : 3) : 1; + + myPriorityEncoder[x][enabled] = color; + } + else + { + uInt8 color = 0; + + if((enabled & myBLBit) != 0) + color = 1; + if((enabled & myPFBit) != 0) + color = (enabled & ScoreBit) ? ((x == 0) ? 2 : 3) : 1; + if((enabled & (myP1Bit | myM1Bit)) != 0) + color = 3; + if((enabled & (myP0Bit | myM0Bit)) != 0) + color = 2; + + myPriorityEncoder[x][enabled] = color; + } + } + } + + for(uInt32 i = 0; i < 640; ++i) + { + ourDisabledMaskTable[i] = 0; + } + + // Compute all of the mask tables + computeBallMaskTable(); + computeCollisionTable(); + computeMissleMaskTable(); + computePlayerMaskTable(); + computePlayerPositionResetWhenTable(); + computePlayerReflectTable(); + computePlayfieldMaskTable(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +TIA::~TIA() +{ + delete[] myCurrentFrameBuffer; + delete[] myPreviousFrameBuffer; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* TIA::name() const +{ + return "TIA"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::reset() +{ + // Clear frame buffers + for(uInt32 i = 0; i < 160 * 300; ++i) + { + myCurrentFrameBuffer[i] = myPreviousFrameBuffer[i] = 0; + } + + // Reset pixel pointer and drawing flag + myFramePointer = myCurrentFrameBuffer; + + // Calculate color clock offsets for starting and stoping frame drawing + myStartDisplayOffset = 228 * + atoi(myConsole.properties().get("Display.YStart").c_str()); + myStopDisplayOffset = myStartDisplayOffset + 228 * + atoi(myConsole.properties().get("Display.Height").c_str()); + + // Reasonable values to start and stop the current frame drawing + myClockWhenFrameStarted = mySystem->cycles() * 3; + myClockStartDisplay = myClockWhenFrameStarted + myStartDisplayOffset; + myClockStopDisplay = myClockWhenFrameStarted + myStopDisplayOffset; + myClockAtLastUpdate = myClockWhenFrameStarted; + myClocksToEndOfScanLine = 228; + myVSYNCFinishClock = 0x7FFFFFFF; + myScanlineCountForFrame = 0; + + // Currently no objects are enabled + myEnabledObjects = 0; + + // Some default values for the registers + myVSYNC = 0; + myVBLANK = 0; + myNUSIZ0 = 0; + myNUSIZ1 = 0; + myCOLUP0 = 0; + myCOLUP1 = 0; + myCOLUPF = 0; + myPlayfieldPriorityAndScore = 0; + myCOLUBK = 0; + myCTRLPF = 0; + myREFP0 = false; + myREFP1 = false; + myPF = 0; + myGRP0 = 0; + myGRP1 = 0; + myDGRP0 = 0; + myDGRP1 = 0; + myENAM0 = false; + myENAM1 = false; + myENABL = false; + myDENABL = false; + myHMP0 = 0; + myHMP1 = 0; + myHMM0 = 0; + myHMM1 = 0; + myHMBL = 0; + myVDELP0 = false; + myVDELP1 = false; + myVDELBL = false; + myRESMP0 = false; + myRESMP1 = false; + myCollision = 0; + myPOSP0 = 0; + myPOSP1 = 0; + myPOSM0 = 0; + myPOSM1 = 0; + myPOSBL = 0; + + + // Some default values for the "current" variables + myCurrentGRP0 = 0; + myCurrentGRP1 = 0; + myCurrentBLMask = ourBallMaskTable[0][0]; + myCurrentM0Mask = ourMissleMaskTable[0][0][0]; + myCurrentM1Mask = ourMissleMaskTable[0][0][0]; + myCurrentP0Mask = ourPlayerMaskTable[0][0][0]; + myCurrentP1Mask = ourPlayerMaskTable[0][0][0]; + myCurrentPFMask = ourPlayfieldTable[0]; + + myLastHMOVEClock = 0; + myHMOVEBlankEnabled = false; + myM0CosmicArkMotionEnabled = false; + myM0CosmicArkCounter = 0; + + myDumpEnabled = false; + myDumpDisabledCycle = 0; + + myAllowHMOVEBlanks = + (myConsole.properties().get("Emulation.HmoveBlanks") == "Yes"); + + myFrameXStart = atoi(myConsole.properties().get("Display.XStart").c_str()); + myFrameWidth = atoi(myConsole.properties().get("Display.Width").c_str()); + myFrameYStart = atoi(myConsole.properties().get("Display.YStart").c_str()); + myFrameHeight = atoi(myConsole.properties().get("Display.Height").c_str()); + + // Make sure the starting x and width values are reasonable + if((myFrameXStart + myFrameWidth) > 160) + { + // Values are illegal so reset to default values + myFrameXStart = 0; + myFrameWidth = 160; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::systemCyclesReset() +{ + // Get the current system cycle + uInt32 cycles = mySystem->cycles(); + + // Adjust the dump cycle + myDumpDisabledCycle -= cycles; + + // Get the current color clock the system is using + uInt32 clocks = cycles * 3; + + // Adjust the clocks by this amount since we're reseting the clock to zero + myClockWhenFrameStarted -= clocks; + myClockStartDisplay -= clocks; + myClockStopDisplay -= clocks; + myClockAtLastUpdate -= clocks; + myVSYNCFinishClock -= clocks; + myLastHMOVEClock -= clocks; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::install(System& system) +{ + // Remember which system I'm installed in + mySystem = &system; + + uInt16 shift = mySystem->pageShift(); + mySystem->resetCycles(); + + + // All accesses are to this device + System::PageAccess access; + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = this; + + // We're installing in a 2600 system + for(uInt32 i = 0; i < 8192; i += (1 << shift)) + { + if((i & 0x1080) == 0x0000) + { + mySystem->setPageAccess(i >> shift, access); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::update() +{ + uInt8* tmp = myCurrentFrameBuffer; + myCurrentFrameBuffer = myPreviousFrameBuffer; + myPreviousFrameBuffer = tmp; + + // Remember the number of clocks which have passed on the current scanline + // so that we can adjust the frame's starting clock by this amount. This + // is necessary since some games position objects during VSYNC and the + // TIA's internal counters are not reset by VSYNC. + uInt32 clocks = ((mySystem->cycles() * 3) - myClockWhenFrameStarted) % 228; + + // Ask the system to reset the cycle count so it doesn't overflow + mySystem->resetCycles(); + + // Setup clocks that'll be used for drawing this frame + myClockWhenFrameStarted = -clocks; + myClockStartDisplay = myClockWhenFrameStarted + myStartDisplayOffset; + myClockStopDisplay = myClockWhenFrameStarted + myStopDisplayOffset; + myClockAtLastUpdate = myClockStartDisplay; + myClocksToEndOfScanLine = 228; + + // Reset frame buffer pointer + myFramePointer = myCurrentFrameBuffer; + + // Execute instructions until frame is finished + mySystem->m6502().execute(25000); + + // TODO: have code here that handles errors.... + + // Compute the number of scanlines in the frame + uInt32 totalClocks = (mySystem->cycles() * 3) - myClockWhenFrameStarted; + myScanlineCountForFrame = totalClocks / 228; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const uInt32* TIA::palette() const +{ + // See which palette we should be using based on properties + if(myConsole.properties().get("Display.Format") == "PAL") + { + return ourPALPalette; + } + else + { + return ourNTSCPalette; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 TIA::width() const +{ + return myFrameWidth; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 TIA::height() const +{ + return myFrameHeight; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 TIA::scanlines() const +{ + return (uInt32)myScanlineCountForFrame; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computeBallMaskTable() +{ + // First, calculate masks for alignment 0 + for(Int32 size = 0; size < 4; ++size) + { + Int32 x; + + // Set all of the masks to false to start with + for(x = 0; x < 160; ++x) + { + ourBallMaskTable[0][size][x] = false; + } + + // Set the necessary fields true + for(x = 0; x < 160 + 8; ++x) + { + if((x >= 0) && (x < (1 << size))) + { + ourBallMaskTable[0][size][x % 160] = true; + } + } + + // Copy fields into the wrap-around area of the mask + for(x = 0; x < 160; ++x) + { + ourBallMaskTable[0][size][x + 160] = ourBallMaskTable[0][size][x]; + } + } + + // Now, copy data for alignments of 1, 2 and 3 + for(uInt32 align = 1; align < 4; ++align) + { + for(uInt32 size = 0; size < 4; ++size) + { + for(uInt32 x = 0; x < 320; ++x) + { + ourBallMaskTable[align][size][x] = + ourBallMaskTable[0][size][(x + 320 - align) % 320]; + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computeCollisionTable() +{ + for(uInt8 i = 0; i < 64; ++i) + { + ourCollisionTable[i] = 0; + + if((i & myM0Bit) && (i & myP1Bit)) // M0-P1 + ourCollisionTable[i] |= 0x0001; + + if((i & myM0Bit) && (i & myP0Bit)) // M0-P0 + ourCollisionTable[i] |= 0x0002; + + if((i & myM1Bit) && (i & myP0Bit)) // M1-P0 + ourCollisionTable[i] |= 0x0004; + + if((i & myM1Bit) && (i & myP1Bit)) // M1-P1 + ourCollisionTable[i] |= 0x0008; + + if((i & myP0Bit) && (i & myPFBit)) // P0-PF + ourCollisionTable[i] |= 0x0010; + + if((i & myP0Bit) && (i & myBLBit)) // P0-BL + ourCollisionTable[i] |= 0x0020; + + if((i & myP1Bit) && (i & myPFBit)) // P1-PF + ourCollisionTable[i] |= 0x0040; + + if((i & myP1Bit) && (i & myBLBit)) // P1-BL + ourCollisionTable[i] |= 0x0080; + + if((i & myM0Bit) && (i & myPFBit)) // M0-PF + ourCollisionTable[i] |= 0x0100; + + if((i & myM0Bit) && (i & myBLBit)) // M0-BL + ourCollisionTable[i] |= 0x0200; + + if((i & myM1Bit) && (i & myPFBit)) // M1-PF + ourCollisionTable[i] |= 0x0400; + + if((i & myM1Bit) && (i & myBLBit)) // M1-BL + ourCollisionTable[i] |= 0x0800; + + if((i & myBLBit) && (i & myPFBit)) // BL-PF + ourCollisionTable[i] |= 0x1000; + + if((i & myP0Bit) && (i & myP1Bit)) // P0-P1 + ourCollisionTable[i] |= 0x2000; + + if((i & myM0Bit) && (i & myM1Bit)) // M0-M1 + ourCollisionTable[i] |= 0x4000; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computeMissleMaskTable() +{ + // First, calculate masks for alignment 0 + Int32 x, size, number; + + // Clear the missle table to start with + for(number = 0; number < 8; ++number) + for(size = 0; size < 4; ++size) + for(x = 0; x < 160; ++x) + ourMissleMaskTable[0][number][size][x] = false; + + for(number = 0; number < 8; ++number) + { + for(size = 0; size < 4; ++size) + { + for(x = 0; x < 160 + 72; ++x) + { + // Only one copy of the missle + if((number == 0x00) || (number == 0x05) || (number == 0x07)) + { + if((x >= 0) && (x < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + } + // Two copies - close + else if(number == 0x01) + { + if((x >= 0) && (x < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 16) >= 0) && ((x - 16) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + } + // Two copies - medium + else if(number == 0x02) + { + if((x >= 0) && (x < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 32) >= 0) && ((x - 32) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + } + // Three copies - close + else if(number == 0x03) + { + if((x >= 0) && (x < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 16) >= 0) && ((x - 16) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 32) >= 0) && ((x - 32) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + } + // Two copies - wide + else if(number == 0x04) + { + if((x >= 0) && (x < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 64) >= 0) && ((x - 64) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + } + // Three copies - medium + else if(number == 0x06) + { + if((x >= 0) && (x < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 32) >= 0) && ((x - 32) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + else if(((x - 64) >= 0) && ((x - 64) < (1 << size))) + ourMissleMaskTable[0][number][size][x % 160] = true; + } + } + + // Copy data into wrap-around area + for(x = 0; x < 160; ++x) + ourMissleMaskTable[0][number][size][x + 160] = + ourMissleMaskTable[0][number][size][x]; + } + } + + // Now, copy data for alignments of 1, 2 and 3 + for(uInt32 align = 1; align < 4; ++align) + { + for(number = 0; number < 8; ++number) + { + for(size = 0; size < 4; ++size) + { + for(x = 0; x < 320; ++x) + { + ourMissleMaskTable[align][number][size][x] = + ourMissleMaskTable[0][number][size][(x + 320 - align) % 320]; + } + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computePlayerMaskTable() +{ + // First, calculate masks for alignment 0 + Int32 x, enable, mode; + + // Set the player mask table to all zeros + for(enable = 0; enable < 2; ++enable) + for(mode = 0; mode < 8; ++mode) + for(x = 0; x < 160; ++x) + ourPlayerMaskTable[0][enable][mode][x] = 0x00; + + // Now, compute the player mask table + for(enable = 0; enable < 2; ++enable) + { + for(mode = 0; mode < 8; ++mode) + { + for(x = 0; x < 160 + 72; ++x) + { + if(mode == 0x00) + { + if((enable == 0) && (x >= 0) && (x < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x; + } + else if(mode == 0x01) + { + if((enable == 0) && (x >= 0) && (x < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x; + else if(((x - 16) >= 0) && ((x - 16) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 16); + } + else if(mode == 0x02) + { + if((enable == 0) && (x >= 0) && (x < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x; + else if(((x - 32) >= 0) && ((x - 32) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 32); + } + else if(mode == 0x03) + { + if((enable == 0) && (x >= 0) && (x < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x; + else if(((x - 16) >= 0) && ((x - 16) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 16); + else if(((x - 32) >= 0) && ((x - 32) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 32); + } + else if(mode == 0x04) + { + if((enable == 0) && (x >= 0) && (x < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x; + else if(((x - 64) >= 0) && ((x - 64) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 64); + } + else if(mode == 0x05) + { + // For some reason in double size mode the player's output + // is delayed by one pixel thus we use > instead of >= + if((enable == 0) && (x > 0) && (x <= 16)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> ((x - 1)/2); + } + else if(mode == 0x06) + { + if((enable == 0) && (x >= 0) && (x < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> x; + else if(((x - 32) >= 0) && ((x - 32) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 32); + else if(((x - 64) >= 0) && ((x - 64) < 8)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> (x - 64); + } + else if(mode == 0x07) + { + // For some reason in quad size mode the player's output + // is delayed by one pixel thus we use > instead of >= + if((enable == 0) && (x > 0) && (x <= 32)) + ourPlayerMaskTable[0][enable][mode][x % 160] = 0x80 >> ((x - 1)/4); + } + } + + // Copy data into wrap-around area + for(x = 0; x < 160; ++x) + { + ourPlayerMaskTable[0][enable][mode][x + 160] = + ourPlayerMaskTable[0][enable][mode][x]; + } + } + } + + // Now, copy data for alignments of 1, 2 and 3 + for(uInt32 align = 1; align < 4; ++align) + { + for(enable = 0; enable < 2; ++enable) + { + for(mode = 0; mode < 8; ++mode) + { + for(x = 0; x < 320; ++x) + { + ourPlayerMaskTable[align][enable][mode][x] = + ourPlayerMaskTable[0][enable][mode][(x + 320 - align) % 320]; + } + } + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computePlayerPositionResetWhenTable() +{ + uInt32 mode, oldx, newx; + + // Loop through all player modes, all old player positions, and all new + // player positions and determine where the new position is located: + // 1 means the new position is within the display of an old copy of the + // player, -1 means the new position is within the delay portion of an + // old copy of the player, and 0 means it's neither of these two + for(mode = 0; mode < 8; ++mode) + { + for(oldx = 0; oldx < 160; ++oldx) + { + // Set everything to 0 for non-delay/non-display section + for(newx = 0; newx < 160; ++newx) + { + ourPlayerPositionResetWhenTable[mode][oldx][newx] = 0; + } + + // Now, we'll set the entries for non-delay/non-display section + for(newx = 0; newx < 160 + 72 + 5; ++newx) + { + if(mode == 0x00) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x01) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 16)) && (newx < (oldx + 16 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 16 + 4) && (newx < (oldx + 16 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x02) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 32)) && (newx < (oldx + 32 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 32 + 4) && (newx < (oldx + 32 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x03) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 16)) && (newx < (oldx + 16 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 32)) && (newx < (oldx + 32 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 16 + 4) && (newx < (oldx + 16 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 32 + 4) && (newx < (oldx + 32 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x04) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 64)) && (newx < (oldx + 64 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 64 + 4) && (newx < (oldx + 64 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x05) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 16))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x06) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 32)) && (newx < (oldx + 32 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + else if((newx >= (oldx + 64)) && (newx < (oldx + 64 + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 32 + 4) && (newx < (oldx + 32 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + else if((newx >= oldx + 64 + 4) && (newx < (oldx + 64 + 4 + 8))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + else if(mode == 0x07) + { + if((newx >= oldx) && (newx < (oldx + 4))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = -1; + + if((newx >= oldx + 4) && (newx < (oldx + 4 + 32))) + ourPlayerPositionResetWhenTable[mode][oldx][newx % 160] = 1; + } + } + + // Let's do a sanity check on our table entries + uInt32 s1 = 0, s2 = 0; + for(newx = 0; newx < 160; ++newx) + { + if(ourPlayerPositionResetWhenTable[mode][oldx][newx] == -1) + ++s1; + if(ourPlayerPositionResetWhenTable[mode][oldx][newx] == 1) + ++s2; + } + assert((s1 % 4 == 0) && (s2 % 8 == 0)); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computePlayerReflectTable() +{ + for(uInt16 i = 0; i < 256; ++i) + { + uInt8 r = 0; + + for(uInt16 t = 1; t <= 128; t *= 2) + { + r = (r << 1) | ((i & t) ? 0x01 : 0x00); + } + + ourPlayerReflectTable[i] = r; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::computePlayfieldMaskTable() +{ + Int32 x; + + // Compute playfield mask table for non-reflected mode + for(x = 0; x < 160; ++x) + { + if(x < 16) + ourPlayfieldTable[0][x] = 0x00001 << (x / 4); + else if(x < 48) + ourPlayfieldTable[0][x] = 0x00800 >> ((x - 16) / 4); + else if(x < 80) + ourPlayfieldTable[0][x] = 0x01000 << ((x - 48) / 4); + else if(x < 96) + ourPlayfieldTable[0][x] = 0x00001 << ((x - 80) / 4); + else if(x < 128) + ourPlayfieldTable[0][x] = 0x00800 >> ((x - 96) / 4); + else if(x < 160) + ourPlayfieldTable[0][x] = 0x01000 << ((x - 128) / 4); + } + + // Compute playfield mask table for reflected mode + for(x = 0; x < 160; ++x) + { + if(x < 16) + ourPlayfieldTable[1][x] = 0x00001 << (x / 4); + else if(x < 48) + ourPlayfieldTable[1][x] = 0x00800 >> ((x - 16) / 4); + else if(x < 80) + ourPlayfieldTable[1][x] = 0x01000 << ((x - 48) / 4); + else if(x < 112) + ourPlayfieldTable[1][x] = 0x80000 >> ((x - 80) / 4); + else if(x < 144) + ourPlayfieldTable[1][x] = 0x00010 << ((x - 112) / 4); + else if(x < 160) + ourPlayfieldTable[1][x] = 0x00008 >> ((x - 144) / 4); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline void TIA::updateFrameScanline(uInt32 clocksToUpdate, uInt32 hpos) +{ + // Calculate the ending frame pointer value + uInt8* ending = myFramePointer + clocksToUpdate; + + // See if we're in the vertical blank region + if(myVBLANK & 0x02) + { + memset(myFramePointer, 0, clocksToUpdate); + } + // Handle all other possible combinations + else + { + switch(myEnabledObjects | myPlayfieldPriorityAndScore) + { + // Background + case 0x00: + case 0x00 | ScoreBit: + case 0x00 | PriorityBit: + case 0x00 | PriorityBit | ScoreBit: + { + memset(myFramePointer, myCOLUBK, clocksToUpdate); + break; + } + + // Playfield is enabled and the score bit is not set + case myPFBit: + case myPFBit | PriorityBit: + { + uInt32* mask = &myCurrentPFMask[hpos]; + + // Update a uInt8 at a time until reaching a uInt32 boundary + for(; ((int)myFramePointer & 0x03) && (myFramePointer < ending); + ++myFramePointer, ++mask) + { + *myFramePointer = (myPF & *mask) ? myCOLUPF : myCOLUBK; + } + + // Now, update a uInt32 at a time + for(; myFramePointer < ending; myFramePointer += 4, mask += 4) + { + *((uInt32*)myFramePointer) = (myPF & *mask) ? myCOLUPF : myCOLUBK; + } + break; + } + + // Playfield is enabled and the score bit is set + case myPFBit | ScoreBit: + case myPFBit | ScoreBit | PriorityBit: + { + uInt32* mask = &myCurrentPFMask[hpos]; + + // Update a uInt8 at a time until reaching a uInt32 boundary + for(; ((int)myFramePointer & 0x03) && (myFramePointer < ending); + ++myFramePointer, ++mask, ++hpos) + { + *myFramePointer = (myPF & *mask) ? + (hpos < 80 ? myCOLUP0 : myCOLUP1) : myCOLUBK; + } + + // Now, update a uInt32 at a time + for(; myFramePointer < ending; + myFramePointer += 4, mask += 4, hpos += 4) + { + *((uInt32*)myFramePointer) = (myPF & *mask) ? + (hpos < 80 ? myCOLUP0 : myCOLUP1) : myCOLUBK; + } + break; + } + + // Player 0 is enabled + case myP0Bit: + case myP0Bit | ScoreBit: + case myP0Bit | PriorityBit: + case myP0Bit | ScoreBit | PriorityBit: + { + uInt8* mP0 = &myCurrentP0Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP0) + { + *(uInt32*)myFramePointer = myCOLUBK; + mP0 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myCurrentGRP0 & *mP0) ? myCOLUP0 : myCOLUBK; + ++mP0; ++myFramePointer; + } + } + break; + } + + // Player 1 is enabled + case myP1Bit: + case myP1Bit | ScoreBit: + case myP1Bit | PriorityBit: + case myP1Bit | ScoreBit | PriorityBit: + { + uInt8* mP1 = &myCurrentP1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP1) + { + *(uInt32*)myFramePointer = myCOLUBK; + mP1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK; + ++mP1; ++myFramePointer; + } + } + break; + } + + // Player 0 and 1 are enabled + case myP0Bit | myP1Bit: + case myP0Bit | myP1Bit | ScoreBit: + case myP0Bit | myP1Bit | PriorityBit: + case myP0Bit | myP1Bit | ScoreBit | PriorityBit: + { + uInt8* mP0 = &myCurrentP0Mask[hpos]; + uInt8* mP1 = &myCurrentP1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP0 && + !*(uInt32*)mP1) + { + *(uInt32*)myFramePointer = myCOLUBK; + mP0 += 4; mP1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myCurrentGRP0 & *mP0) ? + myCOLUP0 : ((myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK); + + if((myCurrentGRP0 & *mP0) && (myCurrentGRP1 & *mP1)) + myCollision |= ourCollisionTable[myP0Bit | myP1Bit]; + + ++mP0; ++mP1; ++myFramePointer; + } + } + break; + } + + // Missle 0 is enabled + case myM0Bit: + case myM0Bit | ScoreBit: + case myM0Bit | PriorityBit: + case myM0Bit | ScoreBit | PriorityBit: + { + uInt8* mM0 = &myCurrentM0Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mM0) + { + *(uInt32*)myFramePointer = myCOLUBK; + mM0 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = *mM0 ? myCOLUP0 : myCOLUBK; + ++mM0; ++myFramePointer; + } + } + break; + } + + // Missle 1 is enabled + case myM1Bit: + case myM1Bit | ScoreBit: + case myM1Bit | PriorityBit: + case myM1Bit | ScoreBit | PriorityBit: + { + uInt8* mM1 = &myCurrentM1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mM1) + { + *(uInt32*)myFramePointer = myCOLUBK; + mM1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = *mM1 ? myCOLUP1 : myCOLUBK; + ++mM1; ++myFramePointer; + } + } + break; + } + + // Ball is enabled + case myBLBit: + case myBLBit | ScoreBit: + case myBLBit | PriorityBit: + case myBLBit | ScoreBit | PriorityBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mBL) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; myFramePointer += 4; + } + else + { + *myFramePointer = *mBL ? myCOLUPF : myCOLUBK; + ++mBL; ++myFramePointer; + } + } + break; + } + + // Missle 0 and 1 are enabled + case myM0Bit | myM1Bit: + case myM0Bit | myM1Bit | ScoreBit: + case myM0Bit | myM1Bit | PriorityBit: + case myM0Bit | myM1Bit | ScoreBit | PriorityBit: + { + uInt8* mM0 = &myCurrentM0Mask[hpos]; + uInt8* mM1 = &myCurrentM1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mM0 && !*(uInt32*)mM1) + { + *(uInt32*)myFramePointer = myCOLUBK; + mM0 += 4; mM1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = *mM0 ? myCOLUP0 : (*mM1 ? myCOLUP1 : myCOLUBK); + + if(*mM0 && *mM1) + myCollision |= ourCollisionTable[myM0Bit | myM1Bit]; + + ++mM0; ++mM1; ++myFramePointer; + } + } + break; + } + + // Ball and Missle 0 are enabled and playfield priority is not set + case myBLBit | myM0Bit: + case myBLBit | myM0Bit | ScoreBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + uInt8* mM0 = &myCurrentM0Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mBL && !*(uInt32*)mM0) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; mM0 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (*mM0 ? myCOLUP0 : (*mBL ? myCOLUPF : myCOLUBK)); + + if(*mBL && *mM0) + myCollision |= ourCollisionTable[myBLBit | myM0Bit]; + + ++mBL; ++mM0; ++myFramePointer; + } + } + break; + } + + // Ball and Missle 0 are enabled and playfield priority is set + case myBLBit | myM0Bit | PriorityBit: + case myBLBit | myM0Bit | ScoreBit | PriorityBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + uInt8* mM0 = &myCurrentM0Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mBL && !*(uInt32*)mM0) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; mM0 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (*mBL ? myCOLUPF : (*mM0 ? myCOLUP0 : myCOLUBK)); + + if(*mBL && *mM0) + myCollision |= ourCollisionTable[myBLBit | myM0Bit]; + + ++mBL; ++mM0; ++myFramePointer; + } + } + break; + } + + // Ball and Missle 1 are enabled and playfield priority is not set + case myBLBit | myM1Bit: + case myBLBit | myM1Bit | ScoreBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + uInt8* mM1 = &myCurrentM1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mBL && + !*(uInt32*)mM1) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; mM1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (*mM1 ? myCOLUP1 : (*mBL ? myCOLUPF : myCOLUBK)); + + if(*mBL && *mM1) + myCollision |= ourCollisionTable[myBLBit | myM1Bit]; + + ++mBL; ++mM1; ++myFramePointer; + } + } + break; + } + + // Ball and Missle 1 are enabled and playfield priority is set + case myBLBit | myM1Bit | PriorityBit: + case myBLBit | myM1Bit | ScoreBit | PriorityBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + uInt8* mM1 = &myCurrentM1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mBL && + !*(uInt32*)mM1) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; mM1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (*mBL ? myCOLUPF : (*mM1 ? myCOLUP1 : myCOLUBK)); + + if(*mBL && *mM1) + myCollision |= ourCollisionTable[myBLBit | myM1Bit]; + + ++mBL; ++mM1; ++myFramePointer; + } + } + break; + } + + // Ball and Player 1 are enabled and playfield priority is not set + case myBLBit | myP1Bit: + case myBLBit | myP1Bit | ScoreBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + uInt8* mP1 = &myCurrentP1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP1 && !*(uInt32*)mBL) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; mP1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myCurrentGRP1 & *mP1) ? myCOLUP1 : + (*mBL ? myCOLUPF : myCOLUBK); + + if(*mBL && (myCurrentGRP1 & *mP1)) + myCollision |= ourCollisionTable[myBLBit | myP1Bit]; + + ++mBL; ++mP1; ++myFramePointer; + } + } + break; + } + + // Ball and Player 1 are enabled and playfield priority is set + case myBLBit | myP1Bit | PriorityBit: + case myBLBit | myP1Bit | PriorityBit | ScoreBit: + { + uInt8* mBL = &myCurrentBLMask[hpos]; + uInt8* mP1 = &myCurrentP1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP1 && !*(uInt32*)mBL) + { + *(uInt32*)myFramePointer = myCOLUBK; + mBL += 4; mP1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = *mBL ? myCOLUPF : + ((myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK); + + if(*mBL && (myCurrentGRP1 & *mP1)) + myCollision |= ourCollisionTable[myBLBit | myP1Bit]; + + ++mBL; ++mP1; ++myFramePointer; + } + } + break; + } + + // Playfield and Player 0 are enabled and playfield priority is not set + case myPFBit | myP0Bit: + { + uInt32* mPF = &myCurrentPFMask[hpos]; + uInt8* mP0 = &myCurrentP0Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP0) + { + *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK; + mPF += 4; mP0 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myCurrentGRP0 & *mP0) ? + myCOLUP0 : ((myPF & *mPF) ? myCOLUPF : myCOLUBK); + + if((myPF & *mPF) && (myCurrentGRP0 & *mP0)) + myCollision |= ourCollisionTable[myPFBit | myP0Bit]; + + ++mPF; ++mP0; ++myFramePointer; + } + } + + break; + } + + // Playfield and Player 0 are enabled and playfield priority is set + case myPFBit | myP0Bit | PriorityBit: + { + uInt32* mPF = &myCurrentPFMask[hpos]; + uInt8* mP0 = &myCurrentP0Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP0) + { + *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK; + mPF += 4; mP0 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myPF & *mPF) ? myCOLUPF : + ((myCurrentGRP0 & *mP0) ? myCOLUP0 : myCOLUBK); + + if((myPF & *mPF) && (myCurrentGRP0 & *mP0)) + myCollision |= ourCollisionTable[myPFBit | myP0Bit]; + + ++mPF; ++mP0; ++myFramePointer; + } + } + + break; + } + + // Playfield and Player 1 are enabled and playfield priority is not set + case myPFBit | myP1Bit: + { + uInt32* mPF = &myCurrentPFMask[hpos]; + uInt8* mP1 = &myCurrentP1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP1) + { + *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK; + mPF += 4; mP1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myCurrentGRP1 & *mP1) ? + myCOLUP1 : ((myPF & *mPF) ? myCOLUPF : myCOLUBK); + + if((myPF & *mPF) && (myCurrentGRP1 & *mP1)) + myCollision |= ourCollisionTable[myPFBit | myP1Bit]; + + ++mPF; ++mP1; ++myFramePointer; + } + } + + break; + } + + // Playfield and Player 1 are enabled and playfield priority is set + case myPFBit | myP1Bit | PriorityBit: + { + uInt32* mPF = &myCurrentPFMask[hpos]; + uInt8* mP1 = &myCurrentP1Mask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mP1) + { + *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK; + mPF += 4; mP1 += 4; myFramePointer += 4; + } + else + { + *myFramePointer = (myPF & *mPF) ? myCOLUPF : + ((myCurrentGRP1 & *mP1) ? myCOLUP1 : myCOLUBK); + + if((myPF & *mPF) && (myCurrentGRP1 & *mP1)) + myCollision |= ourCollisionTable[myPFBit | myP1Bit]; + + ++mPF; ++mP1; ++myFramePointer; + } + } + + break; + } + + // Playfield and Ball are enabled + case myPFBit | myBLBit: + case myPFBit | myBLBit | PriorityBit: + { + uInt32* mPF = &myCurrentPFMask[hpos]; + uInt8* mBL = &myCurrentBLMask[hpos]; + + while(myFramePointer < ending) + { + if(!((int)myFramePointer & 0x03) && !*(uInt32*)mBL) + { + *(uInt32*)myFramePointer = (myPF & *mPF) ? myCOLUPF : myCOLUBK; + mPF += 4; mBL += 4; myFramePointer += 4; + } + else + { + *myFramePointer = ((myPF & *mPF) || *mBL) ? myCOLUPF : myCOLUBK; + + if((myPF & *mPF) && *mBL) + myCollision |= ourCollisionTable[myPFBit | myBLBit]; + + ++mPF; ++mBL; ++myFramePointer; + } + } + break; + } + + // Handle all of the other cases + default: + { + for(; myFramePointer < ending; ++myFramePointer, ++hpos) + { + uInt8 enabled = (myPF & myCurrentPFMask[hpos]) ? myPFBit : 0; + + if((myEnabledObjects & myBLBit) && myCurrentBLMask[hpos]) + enabled |= myBLBit; + + if(myCurrentGRP1 & myCurrentP1Mask[hpos]) + enabled |= myP1Bit; + + if((myEnabledObjects & myM1Bit) && myCurrentM1Mask[hpos]) + enabled |= myM1Bit; + + if(myCurrentGRP0 & myCurrentP0Mask[hpos]) + enabled |= myP0Bit; + + if((myEnabledObjects & myM0Bit) && myCurrentM0Mask[hpos]) + enabled |= myM0Bit; + + myCollision |= ourCollisionTable[enabled]; + + *myFramePointer = myColor[myPriorityEncoder[hpos < 80 ? 0 : 1] + [enabled | myPlayfieldPriorityAndScore]]; + } + break; + } + } + } + myFramePointer = ending; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline void TIA::updateFrame(Int32 clock) +{ + // See if we're in the nondisplayable portion of the screen or if + // we've already updated this portion of the screen + if((clock < myClockStartDisplay) || + (myClockAtLastUpdate >= myClockStopDisplay) || + (myClockAtLastUpdate >= clock)) + { + return; + } + + // Truncate the number of cycles to update to the stop display point + if(clock > myClockStopDisplay) + { + clock = myClockStopDisplay; + } + + // Update frame one scanline at a time + do + { + // Compute the number of clocks we're going to update + Int32 clocksToUpdate = 0; + + // Remember how many clocks we are from the left side of the screen + Int32 clocksFromStartOfScanLine = 228 - myClocksToEndOfScanLine; + + // See if we're updating more than the current scanline + if(clock > (myClockAtLastUpdate + myClocksToEndOfScanLine)) + { + // Yes, we have more than one scanline to update so finish current one + clocksToUpdate = myClocksToEndOfScanLine; + myClocksToEndOfScanLine = 228; + myClockAtLastUpdate += clocksToUpdate; + } + else + { + // No, so do as much of the current scanline as possible + clocksToUpdate = clock - myClockAtLastUpdate; + myClocksToEndOfScanLine -= clocksToUpdate; + myClockAtLastUpdate = clock; + } + + Int32 startOfScanLine = HBLANK + myFrameXStart; + + // Skip over as many horizontal blank clocks as we can + if(clocksFromStartOfScanLine < startOfScanLine) + { + uInt32 tmp; + + if((startOfScanLine - clocksFromStartOfScanLine) < clocksToUpdate) + tmp = startOfScanLine - clocksFromStartOfScanLine; + else + tmp = clocksToUpdate; + + clocksFromStartOfScanLine += tmp; + clocksToUpdate -= tmp; + + // Handle HMOVE blanks + if(myHMOVEBlankEnabled && (startOfScanLine < HBLANK + 8) && + (clocksFromStartOfScanLine == (Int32)(HBLANK + myFrameXStart))) + { + Int32 blanks = 8 - myFrameXStart; + myHMOVEBlankEnabled = false; + memset(myFramePointer, 0, blanks); + myFramePointer += blanks; + clocksFromStartOfScanLine += blanks; + + if(clocksToUpdate >= blanks) + { + clocksToUpdate -= blanks; + } + else + { + // Updating more that we were supposed to so adjust the clocks + myClocksToEndOfScanLine -= (blanks - clocksToUpdate); + myClockAtLastUpdate += (blanks - clocksToUpdate); + clocksToUpdate = 0; + } + } + } + + // Update as much of the scanline as we can + if(clocksToUpdate != 0) + { + updateFrameScanline(clocksToUpdate, clocksFromStartOfScanLine - HBLANK); + } + + // See if we're at the end of a scanline + if(myClocksToEndOfScanLine == 228) + { + myFramePointer -= (160 - myFrameWidth - myFrameXStart); + + // Yes, so set PF mask based on current CTRLPF reflection state + myCurrentPFMask = ourPlayfieldTable[myCTRLPF & 0x01]; + + // TODO: These should be reset right after the first copy of the player + // has passed. However, for now we'll just reset at the end of the + // scanline since the other way would be to slow (01/21/99). + myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03] + [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)]; + myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03] + [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)]; + + // Handle the "Cosmic Ark" TIA bug if it's enabled + if(myM0CosmicArkMotionEnabled) + { + // Movement table associated with the bug + static uInt32 m[4] = {18, 33, 0, 17}; + + myM0CosmicArkCounter = (myM0CosmicArkCounter + 1) & 3; + myPOSM0 -= m[myM0CosmicArkCounter]; + + if(myPOSM0 >= 160) + myPOSM0 -= 160; + else if(myPOSM0 < 0) + myPOSM0 += 160; + + if(myM0CosmicArkCounter == 1) + { + // Stretch this missle so it's at least 2 pixels wide + myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] + [myNUSIZ0 & 0x07][((myNUSIZ0 & 0x30) >> 4) | 0x01] + [160 - (myPOSM0 & 0xFC)]; + } + else if(myM0CosmicArkCounter == 2) + { + // Missle is disabled on this line + myCurrentM0Mask = &ourDisabledMaskTable[0]; + } + else + { + myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] + [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)]; + } + } + } + } + while(myClockAtLastUpdate < clock); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline void TIA::waitHorizontalSync() +{ + uInt32 cyclesToEndOfLine = 76 - ((mySystem->cycles() - + (myClockWhenFrameStarted / 3)) % 76); + + if(cyclesToEndOfLine < 76) + { + mySystem->incrementCycles(cyclesToEndOfLine); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 TIA::peek(uInt16 addr) +{ + // Update frame to current color clock before we look at anything! + updateFrame(mySystem->cycles() * 3); + + switch(addr & 0x000f) + { + case 0x00: // CXM0P + return ((myCollision & 0x0001) ? 0x80 : 0x00) | + ((myCollision & 0x0002) ? 0x40 : 0x00); + + case 0x01: // CXM1P + return ((myCollision & 0x0004) ? 0x80 : 0x00) | + ((myCollision & 0x0008) ? 0x40 : 0x00) | 0x01; + + case 0x02: // CXP0FB + return ((myCollision & 0x0010) ? 0x80 : 0x00) | + ((myCollision & 0x0020) ? 0x40 : 0x00) | 0x02; + + case 0x03: // CXP1FB + return ((myCollision & 0x0040) ? 0x80 : 0x00) | + ((myCollision & 0x0080) ? 0x40 : 0x00) | 0x03; + + case 0x04: // CXM0FB + return ((myCollision & 0x0100) ? 0x80 : 0x00) | + ((myCollision & 0x0200) ? 0x40 : 0x00) | 0x04; + + case 0x05: // CXM1FB + return ((myCollision & 0x0400) ? 0x80 : 0x00) | + ((myCollision & 0x0800) ? 0x40 : 0x00) | 0x05; + + case 0x06: // CXBLPF + return ((myCollision & 0x1000) ? 0x80 : 0x00) | 0x06; + + case 0x07: // CXPPMM + return ((myCollision & 0x2000) ? 0x80 : 0x00) | + ((myCollision & 0x4000) ? 0x40 : 0x00); + + case 0x08: // INPT0 + { + Int32 r = myConsole.controller(Controller::Left).read(Controller::Nine); + if(r == Controller::minimumResistance) + { + return 0xFF; + } + else if((r == Controller::maximumResistance) || myDumpEnabled) + { + return 0x7F; + } + else + { + double t = (1.6 * r * 0.01E-6); + uInt32 needed = (uInt32)(t * 1.19E6); + if(mySystem->cycles() > (myDumpDisabledCycle + needed)) + { + return 0xFF; + } + else + { + return 0x7F; + } + } + } + + case 0x09: // INPT1 + { + Int32 r = myConsole.controller(Controller::Left).read(Controller::Five); + if(r == Controller::minimumResistance) + { + return 0xFF; + } + else if((r == Controller::maximumResistance) || myDumpEnabled) + { + return 0x7F; + } + else + { + double t = (1.6 * r * 0.01E-6); + uInt32 needed = (uInt32)(t * 1.19E6); + if(mySystem->cycles() > (myDumpDisabledCycle + needed)) + { + return 0xFF; + } + else + { + return 0x7F; + } + } + } + + case 0x0A: // INPT2 + { + Int32 r = myConsole.controller(Controller::Right).read(Controller::Nine); + if(r == Controller::minimumResistance) + { + return 0xFF; + } + else if((r == Controller::maximumResistance) || myDumpEnabled) + { + return 0x7F; + } + else + { + double t = (1.6 * r * 0.01E-6); + uInt32 needed = (uInt32)(t * 1.19E6); + if(mySystem->cycles() > (myDumpDisabledCycle + needed)) + { + return 0xFF; + } + else + { + return 0x7F; + } + } + } + + case 0x0B: // INPT3 + { + Int32 r = myConsole.controller(Controller::Right).read(Controller::Five); + if(r == Controller::minimumResistance) + { + return 0xFF; + } + else if((r == Controller::maximumResistance) || myDumpEnabled) + { + return 0x7F; + } + else + { + double t = (1.6 * r * 0.01E-6); + uInt32 needed = (uInt32)(t * 1.19E6); + if(mySystem->cycles() > (myDumpDisabledCycle + needed)) + { + return 0xFF; + } + else + { + return 0x7F; + } + } + } + + case 0x0C: // INPT4 + return myConsole.controller(Controller::Left).read(Controller::Six) ? + 0xFF : 0x7F; + + case 0x0D: // INPT5 + return myConsole.controller(Controller::Right).read(Controller::Six) ? + 0xFF : 0x7F; + + case 0x0e: + return 0x0e; + + default: + return 0x0f; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void TIA::poke(uInt16 addr, uInt8 value) +{ + addr = addr & 0x003f; + + Int32 clock = mySystem->cycles() * 3; + Int16 delay = ourPokeDelayTable[addr]; + + // See if this is a poke to a PF register + if(delay == -1) + { + static uInt32 d[4] = {4, 5, 2, 3}; + Int32 x = ((clock - myClockWhenFrameStarted) % 228); + delay = d[(x / 3) & 3]; + } + + // Update frame to current CPU cycle before we make any changes! + updateFrame(clock + delay); + + switch(addr) + { + case 0x00: // Vertical sync set-clear + { + myVSYNC = value; + + if(myVSYNC & 0x02) + { + // Indicate when VSYNC should be finished. This should really + // be 3 * 228 according to Atari's documentation, however, some + // games don't supply the full 3 scanlines of VSYNC. + myVSYNCFinishClock = clock + 228; + } + else if(!(myVSYNC & 0x02) && (clock >= myVSYNCFinishClock)) + { + // We're no longer interested in myVSYNCFinishClock + myVSYNCFinishClock = 0x7FFFFFFF; + + // Since we're finished with the frame tell the processor to halt + mySystem->m6502().stop(); + } + break; + } + + case 0x01: // Vertical blank set-clear + { + // Is the dump to ground path being set for I0, I1, I2, and I3? + if(!(myVBLANK & 0x80) && (value & 0x80)) + { + myDumpEnabled = true; + } + + // Is the dump to ground path being removed from I0, I1, I2, and I3? + if((myVBLANK & 0x80) && !(value & 0x80)) + { + myDumpEnabled = false; + myDumpDisabledCycle = mySystem->cycles(); + } + + myVBLANK = value; + break; + } + + case 0x02: // Wait for leading edge of HBLANK + { + // Tell the cpu to waste the necessary amount of time + waitHorizontalSync(); + break; + } + + case 0x03: // Reset horizontal sync counter + { +// cerr << "TIA Poke: " << hex << addr << endl; + break; + } + + case 0x04: // Number-size of player-missle 0 + { + myNUSIZ0 = value; + + // TODO: Technically the "enable" part, [0], should depend on the current + // enabled or disabled state. This mean we probably need a data member + // to maintain that state (01/21/99). + myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03] + [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)]; + + myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] + [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)]; + + break; + } + + case 0x05: // Number-size of player-missle 1 + { + myNUSIZ1 = value; + + // TODO: Technically the "enable" part, [0], should depend on the current + // enabled or disabled state. This mean we probably need a data member + // to maintain that state (01/21/99). + myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03] + [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)]; + + myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03] + [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)]; + + break; + } + + case 0x06: // Color-Luminance Player 0 + { + uInt32 color = (uInt32)value; + myCOLUP0 = (((((color << 8) | color) << 8) | color) << 8) | color; + break; + } + + case 0x07: // Color-Luminance Player 1 + { + uInt32 color = (uInt32)value; + myCOLUP1 = (((((color << 8) | color) << 8) | color) << 8) | color; + break; + } + + case 0x08: // Color-Luminance Playfield + { + uInt32 color = (uInt32)value; + myCOLUPF = (((((color << 8) | color) << 8) | color) << 8) | color; + break; + } + + case 0x09: // Color-Luminance Background + { + uInt32 color = (uInt32)value; + myCOLUBK = (((((color << 8) | color) << 8) | color) << 8) | color; + break; + } + + case 0x0A: // Control Playfield, Ball size, Collisions + { + myCTRLPF = value; + + // The playfield priority and score bits from the control register + // are accessed when the frame is being drawn. We precompute the + // necessary value here so we can save time while drawing. + myPlayfieldPriorityAndScore = ((myCTRLPF & 0x06) << 5); + + // Update the playfield mask based on reflection state if + // we're still on the left hand side of the playfield + if(((clock - myClockWhenFrameStarted) % 228) < (68 + 79)) + { + myCurrentPFMask = ourPlayfieldTable[myCTRLPF & 0x01]; + } + + myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03] + [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)]; + + break; + } + + case 0x0B: // Reflect Player 0 + { + // See if the reflection state of the player is being changed + if(((value & 0x08) && !myREFP0) || (!(value & 0x08) && myREFP0)) + { + myREFP0 = (value & 0x08); + myCurrentGRP0 = ourPlayerReflectTable[myCurrentGRP0]; + } + break; + } + + case 0x0C: // Reflect Player 1 + { + // See if the reflection state of the player is being changed + if(((value & 0x08) && !myREFP1) || (!(value & 0x08) && myREFP1)) + { + myREFP1 = (value & 0x08); + myCurrentGRP1 = ourPlayerReflectTable[myCurrentGRP1]; + } + break; + } + + case 0x0D: // Playfield register byte 0 + { + myPF = (myPF & 0x000FFFF0) | ((value >> 4) & 0x0F); + + if(myPF != 0) + myEnabledObjects |= myPFBit; + else + myEnabledObjects &= ~myPFBit; + + break; + } + + case 0x0E: // Playfield register byte 1 + { + myPF = (myPF & 0x000FF00F) | ((uInt32)value << 4); + + if(myPF != 0) + myEnabledObjects |= myPFBit; + else + myEnabledObjects &= ~myPFBit; + + break; + } + + case 0x0F: // Playfield register byte 2 + { + myPF = (myPF & 0x00000FFF) | ((uInt32)value << 12); + + if(myPF != 0) + myEnabledObjects |= myPFBit; + else + myEnabledObjects &= ~myPFBit; + + break; + } + + case 0x10: // Reset Player 0 + { + Int32 hpos = (clock - myClockWhenFrameStarted) % 228; + Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160); + + // Find out under what condition the player is being reset + Int8 when = ourPlayerPositionResetWhenTable[myNUSIZ0 & 7][myPOSP0][newx]; + + // Player is being reset during the display of one of its copies + if(when == 1) + { + // So we go ahead and update the display before moving the player + // TODO: The 11 should depend on how much of the player has already + // been displayed. Probably change table to return the amount to + // delay by instead of just 1 (01/21/99). + updateFrame(clock + 11); + + myPOSP0 = newx; + + // Setup the mask to skip the first copy of the player + myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03] + [1][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)]; + } + // Player is being reset in neither the delay nor display section + else if(when == 0) + { + myPOSP0 = newx; + + // So we setup the mask to skip the first copy of the player + myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03] + [1][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)]; + } + // Player is being reset during the delay section of one of its copies + else if(when == -1) + { + myPOSP0 = newx; + + // So we setup the mask to display all copies of the player + myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03] + [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)]; + } + break; + } + + case 0x11: // Reset Player 1 + { + Int32 hpos = (clock - myClockWhenFrameStarted) % 228; + Int32 newx = hpos < HBLANK ? 3 : (((hpos - HBLANK) + 5) % 160); + + // Find out under what condition the player is being reset + Int8 when = ourPlayerPositionResetWhenTable[myNUSIZ1 & 7][myPOSP1][newx]; + + // Player is being reset during the display of one of its copies + if(when == 1) + { + // So we go ahead and update the display before moving the player + // TODO: The 11 should depend on how much of the player has already + // been displayed. Probably change table to return the amount to + // delay by instead of just 1 (01/21/99). + updateFrame(clock + 11); + + myPOSP1 = newx; + + // Setup the mask to skip the first copy of the player + myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03] + [1][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)]; + } + // Player is being reset in neither the delay nor display section + else if(when == 0) + { + myPOSP1 = newx; + + // So we setup the mask to skip the first copy of the player + myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03] + [1][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)]; + } + // Player is being reset during the delay section of one of its copies + else if(when == -1) + { + myPOSP1 = newx; + + // So we setup the mask to display all copies of the player + myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03] + [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)]; + } + break; + } + + case 0x12: // Reset Missle 0 + { + int hpos = (clock - myClockWhenFrameStarted) % 228; + myPOSM0 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160); + + myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] + [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)]; + break; + } + + case 0x13: // Reset Missle 1 + { + int hpos = (clock - myClockWhenFrameStarted) % 228; + myPOSM1 = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160); + + myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03] + [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)]; + break; + } + + case 0x14: // Reset Ball + { + int hpos = (clock - myClockWhenFrameStarted) % 228 ; + myPOSBL = hpos < HBLANK ? 2 : (((hpos - HBLANK) + 4) % 160); + + // TODO: Remove the following special hack for Escape from the + // Mindmaster by figuring out what really happens when Reset Ball + // occurs 18 cycles after an HMOVE (01/09/99). + if(((clock - myLastHMOVEClock) == (18 * 3)) && + ((hpos == 60) || (hpos == 69))) + { + myPOSBL = 10; + } + + myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03] + [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)]; + break; + } + + case 0x15: // Audio control 0 + { + mySound.set(Sound::AUDC0, value); + break; + } + + case 0x16: // Audio control 1 + { + mySound.set(Sound::AUDC1, value); + break; + } + + case 0x17: // Audio frequency 0 + { + mySound.set(Sound::AUDF0, value); + break; + } + + case 0x18: // Audio frequency 1 + { + mySound.set(Sound::AUDF1, value); + break; + } + + case 0x19: // Audio volume 0 + { + mySound.set(Sound::AUDV0, value); + break; + } + + case 0x1A: // Audio volume 1 + { + mySound.set(Sound::AUDV1, value); + break; + } + + case 0x1B: // Graphics Player 0 + { + // Set player 0 graphics + myGRP0 = value; + + // Copy player 1 graphics into its delayed register + myDGRP1 = myGRP1; + + // Get the "current" data for GRP0 base on delay register and reflect + uInt8 grp0 = myVDELP0 ? myDGRP0 : myGRP0; + myCurrentGRP0 = myREFP0 ? ourPlayerReflectTable[grp0] : grp0; + + // Get the "current" data for GRP1 base on delay register and reflect + uInt8 grp1 = myVDELP1 ? myDGRP1 : myGRP1; + myCurrentGRP1 = myREFP1 ? ourPlayerReflectTable[grp1] : grp1; + + // Set enabled object bits + if(myCurrentGRP0 != 0) + myEnabledObjects |= myP0Bit; + else + myEnabledObjects &= ~myP0Bit; + + if(myCurrentGRP1 != 0) + myEnabledObjects |= myP1Bit; + else + myEnabledObjects &= ~myP1Bit; + + break; + } + + case 0x1C: // Graphics Player 1 + { + // Set player 1 graphics + myGRP1 = value; + + // Copy player 0 graphics into its delayed register + myDGRP0 = myGRP0; + + // Copy ball graphics into its delayed register + myDENABL = myENABL; + + // Get the "current" data for GRP0 base on delay register + uInt8 grp0 = myVDELP0 ? myDGRP0 : myGRP0; + myCurrentGRP0 = myREFP0 ? ourPlayerReflectTable[grp0] : grp0; + + // Get the "current" data for GRP1 base on delay register + uInt8 grp1 = myVDELP1 ? myDGRP1 : myGRP1; + myCurrentGRP1 = myREFP1 ? ourPlayerReflectTable[grp1] : grp1; + + // Set enabled object bits + if(myCurrentGRP0 != 0) + myEnabledObjects |= myP0Bit; + else + myEnabledObjects &= ~myP0Bit; + + if(myCurrentGRP1 != 0) + myEnabledObjects |= myP1Bit; + else + myEnabledObjects &= ~myP1Bit; + + if(myVDELBL ? myDENABL : myENABL) + myEnabledObjects |= myBLBit; + else + myEnabledObjects &= ~myBLBit; + + break; + } + + case 0x1D: // Enable Missle 0 graphics + { + myENAM0 = value & 0x02; + + if(myENAM0 && !myRESMP0) + myEnabledObjects |= myM0Bit; + else + myEnabledObjects &= ~myM0Bit; + break; + } + + case 0x1E: // Enable Missle 1 graphics + { + myENAM1 = value & 0x02; + + if(myENAM1 && !myRESMP1) + myEnabledObjects |= myM1Bit; + else + myEnabledObjects &= ~myM1Bit; + break; + } + + case 0x1F: // Enable Ball graphics + { + myENABL = value & 0x02; + + if(myVDELBL ? myDENABL : myENABL) + myEnabledObjects |= myBLBit; + else + myEnabledObjects &= ~myBLBit; + + break; + } + + case 0x20: // Horizontal Motion Player 0 + { + myHMP0 = value >> 4; + break; + } + + case 0x21: // Horizontal Motion Player 1 + { + myHMP1 = value >> 4; + break; + } + + case 0x22: // Horizontal Motion Missle 0 + { + Int8 tmp = value >> 4; + + // Should we enabled TIA M0 "bug" used for stars in Cosmic Ark? + if((clock == (myLastHMOVEClock + 21 * 3)) && (myHMM0 == 7) && (tmp == 6)) + { + myM0CosmicArkMotionEnabled = true; + myM0CosmicArkCounter = 0; + } + + myHMM0 = tmp; + break; + } + + case 0x23: // Horizontal Motion Missle 1 + { + myHMM1 = value >> 4; + break; + } + + case 0x24: // Horizontal Motion Ball + { + myHMBL = value >> 4; + break; + } + + case 0x25: // Vertial Delay Player 0 + { + myVDELP0 = value & 0x01; + + uInt8 grp0 = myVDELP0 ? myDGRP0 : myGRP0; + myCurrentGRP0 = myREFP0 ? ourPlayerReflectTable[grp0] : grp0; + + if(myCurrentGRP0 != 0) + myEnabledObjects |= myP0Bit; + else + myEnabledObjects &= ~myP0Bit; + break; + } + + case 0x26: // Vertial Delay Player 1 + { + myVDELP1 = value & 0x01; + + uInt8 grp1 = myVDELP1 ? myDGRP1 : myGRP1; + myCurrentGRP1 = myREFP1 ? ourPlayerReflectTable[grp1] : grp1; + + if(myCurrentGRP1 != 0) + myEnabledObjects |= myP1Bit; + else + myEnabledObjects &= ~myP1Bit; + break; + } + + case 0x27: // Vertial Delay Ball + { + myVDELBL = value & 0x01; + + if(myVDELBL ? myDENABL : myENABL) + myEnabledObjects |= myBLBit; + else + myEnabledObjects &= ~myBLBit; + break; + } + + case 0x28: // Reset missle 0 to player 0 + { + if(myRESMP0 && !(value & 0x02)) + { + uInt16 middle; + + if((myNUSIZ0 & 0x07) == 0x05) + middle = 8; + else if((myNUSIZ0 & 0x07) == 0x07) + middle = 16; + else + middle = 4; + + myPOSM0 = (myPOSP0 + middle) % 160; + myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] + [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)]; + } + + myRESMP0 = value & 0x02; + + if(myENAM0 && !myRESMP0) + myEnabledObjects |= myM0Bit; + else + myEnabledObjects &= ~myM0Bit; + + break; + } + + case 0x29: // Reset missle 1 to player 1 + { + if(myRESMP1 && !(value & 0x02)) + { + uInt16 middle; + + if((myNUSIZ1 & 0x07) == 0x05) + middle = 8; + else if((myNUSIZ1 & 0x07) == 0x07) + middle = 16; + else + middle = 4; + + myPOSM1 = (myPOSP1 + middle) % 160; + myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03] + [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)]; + } + + myRESMP1 = value & 0x02; + + if(myENAM1 && !myRESMP1) + myEnabledObjects |= myM1Bit; + else + myEnabledObjects &= ~myM1Bit; + break; + } + + case 0x2A: // Apply horizontal motion + { + // Figure out what cycle we're at + Int32 x = ((clock - myClockWhenFrameStarted) % 228) / 3; + + // See if we need to enable the HMOVE blank bug + if(myAllowHMOVEBlanks && ourHMOVEBlankEnableCycles[x]) + { + // TODO: Allow this to be turned off using properties... + myHMOVEBlankEnabled = true; + } + + myPOSP0 += ourCompleteMotionTable[x][myHMP0]; + myPOSP1 += ourCompleteMotionTable[x][myHMP1]; + myPOSM0 += ourCompleteMotionTable[x][myHMM0]; + myPOSM1 += ourCompleteMotionTable[x][myHMM1]; + myPOSBL += ourCompleteMotionTable[x][myHMBL]; + + if(myPOSP0 >= 160) + myPOSP0 -= 160; + else if(myPOSP0 < 0) + myPOSP0 += 160; + + if(myPOSP1 >= 160) + myPOSP1 -= 160; + else if(myPOSP1 < 0) + myPOSP1 += 160; + + if(myPOSM0 >= 160) + myPOSM0 -= 160; + else if(myPOSM0 < 0) + myPOSM0 += 160; + + if(myPOSM1 >= 160) + myPOSM1 -= 160; + else if(myPOSM1 < 0) + myPOSM1 += 160; + + if(myPOSBL >= 160) + myPOSBL -= 160; + else if(myPOSBL < 0) + myPOSBL += 160; + + myCurrentBLMask = &ourBallMaskTable[myPOSBL & 0x03] + [(myCTRLPF & 0x30) >> 4][160 - (myPOSBL & 0xFC)]; + + myCurrentP0Mask = &ourPlayerMaskTable[myPOSP0 & 0x03] + [0][myNUSIZ0 & 0x07][160 - (myPOSP0 & 0xFC)]; + myCurrentP1Mask = &ourPlayerMaskTable[myPOSP1 & 0x03] + [0][myNUSIZ1 & 0x07][160 - (myPOSP1 & 0xFC)]; + + myCurrentM0Mask = &ourMissleMaskTable[myPOSM0 & 0x03] + [myNUSIZ0 & 0x07][(myNUSIZ0 & 0x30) >> 4][160 - (myPOSM0 & 0xFC)]; + myCurrentM1Mask = &ourMissleMaskTable[myPOSM1 & 0x03] + [myNUSIZ1 & 0x07][(myNUSIZ1 & 0x30) >> 4][160 - (myPOSM1 & 0xFC)]; + + // Remember what clock HMOVE occured at + myLastHMOVEClock = clock; + + // Disable TIA M0 "bug" used for stars in Cosmic ark + myM0CosmicArkMotionEnabled = false; + break; + } + + case 0x2b: // Clear horizontal motion registers + { + myHMP0 = 0; + myHMP1 = 0; + myHMM0 = 0; + myHMM1 = 0; + myHMBL = 0; + break; + } + + case 0x2c: // Clear collision latches + { + myCollision = 0; + break; + } + + default: + { +#ifdef DEBUG_ACCESSES + cerr << "BAD TIA Poke: " << hex << addr << endl; +#endif + break; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 TIA::ourBallMaskTable[4][4][320]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 TIA::ourCollisionTable[64]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 TIA::ourDisabledMaskTable[640]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Int16 TIA::ourPokeDelayTable[64] = { + 0, 0, 0, 0, 12, 12, 0, 0, 0, 0, 0, 1, 1, -1, -1, -1, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 TIA::ourMissleMaskTable[4][8][4][320]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const bool TIA::ourHMOVEBlankEnableCycles[76] = { + true, true, true, true, true, true, true, true, true, true, // 00 + true, true, true, true, true, true, true, true, true, true, // 10 + true, false, false, false, false, false, false, false, false, false, // 20 + false, false, false, false, false, false, false, false, false, false, // 30 + false, false, false, false, false, false, false, false, false, false, // 40 + false, false, false, false, false, false, false, false, false, false, // 50 + false, false, false, false, false, false, false, false, false, false, // 60 + false, false, false, false, false, true // 70 +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const Int32 TIA::ourCompleteMotionTable[76][16] = { + { 0, -1, -2, -3, -4, -5, -6, -7, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -5, -6, -7, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -5, -6, -7, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -5, -6, -7, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -5, -6, -6, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -5, -5, -5, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -5, -5, -5, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -4, -4, -4, -4, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -3, -3, -3, -3, -3, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -2, -2, -2, -2, -2, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -2, -2, -2, -2, -2, -2, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, -1, -1, -1, -1, -1, -1, -1, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 0, 0, 0, 0, 0, 0, 0, 0, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 1, 1, 1, 1, 1, 1, 1, 1, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 1, 1, 1, 1, 1, 1, 1, 1, 8, 7, 6, 5, 4, 3, 2, 1}, // HBLANK + { 2, 2, 2, 2, 2, 2, 2, 2, 8, 7, 6, 5, 4, 3, 2, 2}, // HBLANK + { 3, 3, 3, 3, 3, 3, 3, 3, 8, 7, 6, 5, 4, 3, 3, 3}, // HBLANK + { 4, 4, 4, 4, 4, 4, 4, 4, 8, 7, 6, 5, 4, 4, 4, 4}, // HBLANK + { 4, 4, 4, 4, 4, 4, 4, 4, 8, 7, 6, 5, 4, 4, 4, 4}, // HBLANK + { 5, 5, 5, 5, 5, 5, 5, 5, 8, 7, 6, 5, 5, 5, 5, 5}, // HBLANK + { 6, 6, 6, 6, 6, 6, 6, 6, 8, 7, 6, 6, 6, 6, 6, 6}, // HBLANK + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, 0, -1, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, 0, -1, -2, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, -1, -2, -3, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, 0, -1, -2, -3, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, 0, -1, -2, -3, -4, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, 0, -1, -2, -3, -4, -5, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, -1, -2, -3, -4, -5, -6, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, 0, -1, -2, -3, -4, -5, -6, 0, 0, 0, 0, 0, 0, 0, 0}, + { 0, -1, -2, -3, -4, -5, -6, -7, 0, 0, 0, 0, 0, 0, 0, 0}, + {-1, -2, -3, -4, -5, -6, -7, -8, 0, 0, 0, 0, 0, 0, 0, 0}, + {-2, -3, -4, -5, -6, -7, -8, -9, 0, 0, 0, 0, 0, 0, 0, -1}, + {-2, -3, -4, -5, -6, -7, -8, -9, 0, 0, 0, 0, 0, 0, 0, -1}, + {-3, -4, -5, -6, -7, -8, -9,-10, 0, 0, 0, 0, 0, 0, -1, -2}, + {-4, -5, -6, -7, -8, -9,-10,-11, 0, 0, 0, 0, 0, -1, -2, -3}, + {-5, -6, -7, -8, -9,-10,-11,-12, 0, 0, 0, 0, -1, -2, -3, -4}, + {-5, -6, -7, -8, -9,-10,-11,-12, 0, 0, 0, 0, -1, -2, -3, -4}, + {-6, -7, -8, -9,-10,-11,-12,-13, 0, 0, 0, -1, -2, -3, -4, -5}, + {-7, -8, -9,-10,-11,-12,-13,-14, 0, 0, -1, -2, -3, -4, -5, -6}, + {-8, -9,-10,-11,-12,-13,-14,-15, 0, -1, -2, -3, -4, -5, -6, -7}, + {-8, -9,-10,-11,-12,-13,-14,-15, 0, -1, -2, -3, -4, -5, -6, -7}, + { 0, -1, -2, -3, -4, -5, -6, -7, 8, 7, 6, 5, 4, 3, 2, 1} // HBLANK +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 TIA::ourPlayerMaskTable[4][2][8][320]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Int8 TIA::ourPlayerPositionResetWhenTable[8][160][160]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 TIA::ourPlayerReflectTable[256]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 TIA::ourPlayfieldTable[2][160]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const uInt32 TIA::ourNTSCPalette[256] = { + 0x000000, 0x1c1c1c, 0x393939, 0x595959, + 0x797979, 0x929292, 0xababab, 0xbcbcbc, + 0xcdcdcd, 0xd9d9d9, 0xe6e6e6, 0xececec, + 0xf2f2f2, 0xf8f8f8, 0xffffff, 0xffffff, + 0x391701, 0x5e2304, 0x833008, 0xa54716, + 0xc85f24, 0xe37820, 0xff911d, 0xffab1d, + 0xffc51d, 0xffce34, 0xffd84c, 0xffe651, + 0xfff456, 0xfff977, 0xffff98, 0xffff98, + 0x451904, 0x721e11, 0x9f241e, 0xb33a20, + 0xc85122, 0xe36920, 0xff811e, 0xff8c25, + 0xff982c, 0xffae38, 0xffc545, 0xffc559, + 0xffc66d, 0xffd587, 0xffe4a1, 0xffe4a1, + 0x4a1704, 0x7e1a0d, 0xb21d17, 0xc82119, + 0xdf251c, 0xec3b38, 0xfa5255, 0xfc6161, + 0xff706e, 0xff7f7e, 0xff8f8f, 0xff9d9e, + 0xffabad, 0xffb9bd, 0xffc7ce, 0xffc7ce, + 0x050568, 0x3b136d, 0x712272, 0x8b2a8c, + 0xa532a6, 0xb938ba, 0xcd3ecf, 0xdb47dd, + 0xea51eb, 0xf45ff5, 0xfe6dff, 0xfe7afd, + 0xff87fb, 0xff95fd, 0xffa4ff, 0xffa4ff, + 0x280479, 0x400984, 0x590f90, 0x70249d, + 0x8839aa, 0xa441c3, 0xc04adc, 0xd054ed, + 0xe05eff, 0xe96dff, 0xf27cff, 0xf88aff, + 0xff98ff, 0xfea1ff, 0xfeabff, 0xfeabff, + 0x35088a, 0x420aad, 0x500cd0, 0x6428d0, + 0x7945d0, 0x8d4bd4, 0xa251d9, 0xb058ec, + 0xbe60ff, 0xc56bff, 0xcc77ff, 0xd183ff, + 0xd790ff, 0xdb9dff, 0xdfaaff, 0xdfaaff, + 0x051e81, 0x0626a5, 0x082fca, 0x263dd4, + 0x444cde, 0x4f5aee, 0x5a68ff, 0x6575ff, + 0x7183ff, 0x8091ff, 0x90a0ff, 0x97a9ff, + 0x9fb2ff, 0xafbeff, 0xc0cbff, 0xc0cbff, + 0x0c048b, 0x2218a0, 0x382db5, 0x483ec7, + 0x584fda, 0x6159ec, 0x6b64ff, 0x7a74ff, + 0x8a84ff, 0x918eff, 0x9998ff, 0xa5a3ff, + 0xb1aeff, 0xb8b8ff, 0xc0c2ff, 0xc0c2ff, + 0x1d295a, 0x1d3876, 0x1d4892, 0x1c5cac, + 0x1c71c6, 0x3286cf, 0x489bd9, 0x4ea8ec, + 0x55b6ff, 0x70c7ff, 0x8cd8ff, 0x93dbff, + 0x9bdfff, 0xafe4ff, 0xc3e9ff, 0xc3e9ff, + 0x2f4302, 0x395202, 0x446103, 0x417a12, + 0x3e9421, 0x4a9f2e, 0x57ab3b, 0x5cbd55, + 0x61d070, 0x69e27a, 0x72f584, 0x7cfa8d, + 0x87ff97, 0x9affa6, 0xadffb6, 0xadffb6, + 0x0a4108, 0x0d540a, 0x10680d, 0x137d0f, + 0x169212, 0x19a514, 0x1cb917, 0x1ec919, + 0x21d91b, 0x47e42d, 0x6ef040, 0x78f74d, + 0x83ff5b, 0x9aff7a, 0xb2ff9a, 0xb2ff9a, + 0x04410b, 0x05530e, 0x066611, 0x077714, + 0x088817, 0x099b1a, 0x0baf1d, 0x48c41f, + 0x86d922, 0x8fe924, 0x99f927, 0xa8fc41, + 0xb7ff5b, 0xc9ff6e, 0xdcff81, 0xdcff81, + 0x02350f, 0x073f15, 0x0c4a1c, 0x2d5f1e, + 0x4f7420, 0x598324, 0x649228, 0x82a12e, + 0xa1b034, 0xa9c13a, 0xb2d241, 0xc4d945, + 0xd6e149, 0xe4f04e, 0xf2ff53, 0xf2ff53, + 0x263001, 0x243803, 0x234005, 0x51541b, + 0x806931, 0x978135, 0xaf993a, 0xc2a73e, + 0xd5b543, 0xdbc03d, 0xe1cb38, 0xe2d836, + 0xe3e534, 0xeff258, 0xfbff7d, 0xfbff7d, + 0x401a02, 0x581f05, 0x702408, 0x8d3a13, + 0xab511f, 0xb56427, 0xbf7730, 0xd0853a, + 0xe19344, 0xeda04e, 0xf9ad58, 0xfcb75c, + 0xffc160, 0xffc671, 0xffcb83, 0xffcb83 +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const uInt32 TIA::ourPALPalette[256] = { + 0x000000, 0x000000, 0x242424, 0x242424, + 0x484848, 0x484848, 0x6d6d6d, 0x6d6d6d, + 0x919191, 0x919191, 0xb6b6b6, 0xb6b6b6, + 0xdadada, 0xdadada, 0xffffff, 0xffffff, + 0x000000, 0x000000, 0x242424, 0x242424, + 0x484848, 0x484848, 0x6d6d6d, 0x6d6d6d, + 0x919191, 0x919191, 0xb6b6b6, 0xb6b6b6, + 0xdadada, 0xdadada, 0xffffff, 0xffffff, + 0x4a3700, 0x4a3700, 0x705813, 0x705813, + 0x8c732a, 0x8c732a, 0xa68d46, 0xa68d46, + 0xbea767, 0xbea767, 0xd4c18b, 0xd4c18b, + 0xeadcb3, 0xeadcb3, 0xfff6de, 0xfff6de, + 0x284a00, 0x284a00, 0x44700f, 0x44700f, + 0x5c8c21, 0x5c8c21, 0x74a638, 0x74a638, + 0x8cbe51, 0x8cbe51, 0xa6d46e, 0xa6d46e, + 0xc0ea8e, 0xc0ea8e, 0xdbffb0, 0xdbffb0, + 0x4a1300, 0x4a1300, 0x70280f, 0x70280f, + 0x8c3d21, 0x8c3d21, 0xa65438, 0xa65438, + 0xbe6d51, 0xbe6d51, 0xd4886e, 0xd4886e, + 0xeaa58e, 0xeaa58e, 0xffc4b0, 0xffc4b0, + 0x004a22, 0x004a22, 0x0f703b, 0x0f703b, + 0x218c52, 0x218c52, 0x38a66a, 0x38a66a, + 0x51be83, 0x51be83, 0x6ed49d, 0x6ed49d, + 0x8eeab8, 0x8eeab8, 0xb0ffd4, 0xb0ffd4, + 0x4a0028, 0x4a0028, 0x700f44, 0x700f44, + 0x8c215c, 0x8c215c, 0xa63874, 0xa63874, + 0xbe518c, 0xbe518c, 0xd46ea6, 0xd46ea6, + 0xea8ec0, 0xea8ec0, 0xffb0db, 0xffb0db, + 0x00404a, 0x00404a, 0x0f6370, 0x0f6370, + 0x217e8c, 0x217e8c, 0x3897a6, 0x3897a6, + 0x51afbe, 0x51afbe, 0x6ec7d4, 0x6ec7d4, + 0x8edeea, 0x8edeea, 0xb0f4ff, 0xb0f4ff, + 0x43002c, 0x43002c, 0x650f4b, 0x650f4b, + 0x7e2165, 0x7e2165, 0x953880, 0x953880, + 0xa6519a, 0xa6519a, 0xbf6eb7, 0xbf6eb7, + 0xd38ed3, 0xd38ed3, 0xe5b0f1, 0xe5b0f1, + 0x001d4a, 0x001d4a, 0x0f3870, 0x0f3870, + 0x21538c, 0x21538c, 0x386ea6, 0x386ea6, + 0x518dbe, 0x518dbe, 0x6ea8d4, 0x6ea8d4, + 0x8ec8ea, 0x8ec8ea, 0xb0e9ff, 0xb0e9ff, + 0x37004a, 0x37004a, 0x570f70, 0x570f70, + 0x70218c, 0x70218c, 0x8938a6, 0x8938a6, + 0xa151be, 0xa151be, 0xba6ed4, 0xba6ed4, + 0xd28eea, 0xd28eea, 0xeab0ff, 0xeab0ff, + 0x00184a, 0x00184a, 0x0f2e70, 0x0f2e70, + 0x21448c, 0x21448c, 0x385ba6, 0x385ba6, + 0x5174be, 0x5174be, 0x6e8fd4, 0x6e8fd4, + 0x8eabea, 0x8eabea, 0xb0c9ff, 0xb0c9ff, + 0x13004a, 0x13004a, 0x280f70, 0x280f70, + 0x3d218c, 0x3d218c, 0x5438a6, 0x5438a6, + 0x6d51be, 0x6d51be, 0x886ed4, 0x886ed4, + 0xa58eea, 0xa58eea, 0xc4b0ff, 0xc4b0ff, + 0x00014a, 0x00014a, 0x0f1170, 0x0f1170, + 0x21248c, 0x21248c, 0x383aa6, 0x383aa6, + 0x5153be, 0x5153be, 0x6e70d4, 0x6e70d4, + 0x8e8fea, 0x8e8fea, 0xb0b2ff, 0xb0b2ff, + 0x000000, 0x000000, 0x242424, 0x242424, + 0x484848, 0x484848, 0x6d6d6d, 0x6d6d6d, + 0x919191, 0x919191, 0xb6b6b6, 0xb6b6b6, + 0xdadada, 0xdadada, 0xffffff, 0xffffff, + 0x000000, 0x000000, 0x242424, 0x242424, + 0x484848, 0x484848, 0x6d6d6d, 0x6d6d6d, + 0x919191, 0x919191, 0xb6b6b6, 0xb6b6b6, + 0xdadada, 0xdadada, 0xffffff, 0xffffff +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +TIA::TIA(const TIA& c) + : myConsole(c.myConsole), + mySound(c.mySound), + myCOLUBK(myColor[0]), + myCOLUPF(myColor[1]), + myCOLUP0(myColor[2]), + myCOLUP1(myColor[3]) +{ + assert(false); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +TIA& TIA::operator = (const TIA&) +{ + assert(false); + + return *this; +} + diff --git a/stella/src/emucore/TIA.hxx b/stella/src/emucore/TIA.hxx new file mode 100644 index 000000000..f1b06f4ce --- /dev/null +++ b/stella/src/emucore/TIA.hxx @@ -0,0 +1,422 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: TIA.hxx,v 1.1.1.1 2001-12-27 19:54:25 bwmott Exp $ +//============================================================================ + +#ifndef TIA_HXX +#define TIA_HXX + +class Console; +class System; + +#include "bspf.hxx" +#include "Device.hxx" +#include "MediaSrc.hxx" + +/** + This class is a device that emulates the Television Interface Adapator + found in the Atari 2600 and 7800 consoles. The Television Interface + Adapator is an integrated circuit designed to interface between an + eight bit microprocessor and a television video modulator. It converts + eight bit parallel data into serial outputs for the color, luminosity, + and composite sync required by a video modulator. + + This class outputs the serial data into a frame buffer which can then + be displayed on screen. + + @author Bradford W. Mott + @version $Id: TIA.hxx,v 1.1.1.1 2001-12-27 19:54:25 bwmott Exp $ +*/ +class TIA : public Device , public MediaSource +{ + public: + /** + Create a new TIA for the specified console + + @param console The console the TIA is associated with + */ + TIA(const Console& console, Sound& sound); + + /** + Destructor + */ + virtual ~TIA(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Notification method invoked by the system right before the + system resets its cycle counter to zero. It may be necessary + to override this method for devices that remember cycle counts. + */ + virtual void systemCyclesReset(); + + /** + Install TIA in the specified system. Invoked by the system + when the TIA is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); + + public: + /** + This method should be called at an interval corresponding to + the desired frame rate to update the media source. + */ + virtual void update(); + + /** + Answers the current frame buffer + + @return Pointer to the current frame buffer + */ + uInt8* currentFrameBuffer() const { return myCurrentFrameBuffer; } + + /** + Answers the previous frame buffer + + @return Pointer to the previous frame buffer + */ + uInt8* previousFrameBuffer() const { return myPreviousFrameBuffer; } + + /** + Get the palette which maps frame data to RGB values. + + @return Array of integers which represent the palette (RGB) + */ + virtual const uInt32* palette() const; + + /** + Answers the height of the frame buffer + + @return The frame's height + */ + uInt32 height() const; + + /** + Answers the width of the frame buffer + + @return The frame's width + */ + uInt32 width() const; + + /** + Answers the total number of scanlines the media source generated + in producing the current frame buffer. + + @return The total number of scanlines generated + */ + uInt32 scanlines() const; + + private: + // Compute the ball mask table + void computeBallMaskTable(); + + // Compute the collision decode table + void computeCollisionTable(); + + // Compute the missle mask table + void computeMissleMaskTable(); + + // Compute the player mask table + void computePlayerMaskTable(); + + // Compute the player position reset when table + void computePlayerPositionResetWhenTable(); + + // Compute the player reflect table + void computePlayerReflectTable(); + + // Compute playfield mask table + void computePlayfieldMaskTable(); + + private: + // Update the current frame buffer up to one scanline + void updateFrameScanline(uInt32 clocksToUpdate, uInt32 hpos); + + // Update the current frame buffer to the specified color clock + void updateFrame(Int32 clock); + + // Waste cycles until the current scanline is finished + void waitHorizontalSync(); + + private: + // Console the TIA is associated with + const Console& myConsole; + + // Sound object used by the TIA + Sound& mySound; + + private: + // Pointer to the current frame buffer + uInt8* myCurrentFrameBuffer; + + // Pointer to the previous frame buffer + uInt8* myPreviousFrameBuffer; + + // Pointer to the next pixel that will be drawn in the current frame buffer + uInt8* myFramePointer; + + // Indicates where the scanline should start being displayed + uInt32 myFrameXStart; + + // Indicates the width of the scanline + uInt32 myFrameWidth; + + // Indicated what scanline the frame should start being drawn at + uInt32 myFrameYStart; + + // Indicates the height of the frame in scanlines + uInt32 myFrameHeight; + + private: + // Indicates offset in color clocks when display should begin + uInt32 myStartDisplayOffset; + + // Indicates offset in color clocks when display should stop + uInt32 myStopDisplayOffset; + + private: + // Indicates color clocks when the current frame began + Int32 myClockWhenFrameStarted; + + // Indicates color clocks when frame should begin to be drawn + Int32 myClockStartDisplay; + + // Indicates color clocks when frame should stop being drawn + Int32 myClockStopDisplay; + + // Indicates color clocks when the frame was last updated + Int32 myClockAtLastUpdate; + + // Indicates how many color clocks remain until the end of + // current scanline. This value is valid during the + // displayed portion of the frame. + Int32 myClocksToEndOfScanLine; + + // Indicates the total number of scanlines generated by the last frame + Int32 myScanlineCountForFrame; + + private: + // Color clock when VSYNC ending causes a new frame to be started + Int32 myVSYNCFinishClock; + + private: + enum + { + myP0Bit = 0x01, // Bit for Player 0 + myM0Bit = 0x02, // Bit for Missle 0 + myP1Bit = 0x04, // Bit for Player 1 + myM1Bit = 0x08, // Bit for Missle 1 + myBLBit = 0x10, // Bit for Ball + myPFBit = 0x20, // Bit for Playfield + ScoreBit = 0x40, // Bit for Playfield score mode + PriorityBit = 0x080 // Bit for Playfield priority + }; + + // Bitmap of the objects that should be considered while drawing + uInt8 myEnabledObjects; + + private: + uInt8 myVSYNC; // Holds the VSYNC register value + uInt8 myVBLANK; // Holds the VBLANK register value + + uInt8 myNUSIZ0; // Number and size of player 0 and missle 0 + uInt8 myNUSIZ1; // Number and size of player 1 and missle 1 + + uInt8 myPlayfieldPriorityAndScore; + uInt32 myColor[4]; + uInt8 myPriorityEncoder[2][256]; + + uInt32& myCOLUBK; // Background color register (replicated 4 times) + uInt32& myCOLUPF; // Playfield color register (replicated 4 times) + uInt32& myCOLUP0; // Player 0 color register (replicated 4 times) + uInt32& myCOLUP1; // Player 1 color register (replicated 4 times) + + uInt8 myCTRLPF; // Playfield control register + + bool myREFP0; // Indicates if player 0 is being reflected + bool myREFP1; // Indicates if player 1 is being reflected + + uInt32 myPF; // Playfield graphics (19-12:PF2 11-4:PF1 3-0:PF0) + + uInt8 myGRP0; // Player 0 graphics register + uInt8 myGRP1; // Player 1 graphics register + + uInt8 myDGRP0; // Player 0 delayed graphics register + uInt8 myDGRP1; // Player 1 delayed graphics register + + bool myENAM0; // Indicates if missle 0 is enabled + bool myENAM1; // Indicates if missle 0 is enabled + + bool myENABL; // Indicates if the ball is enabled + bool myDENABL; // Indicates if the virtically delayed ball is enabled + + Int8 myHMP0; // Player 0 horizontal motion register + Int8 myHMP1; // Player 1 horizontal motion register + Int8 myHMM0; // Missle 0 horizontal motion register + Int8 myHMM1; // Missle 1 horizontal motion register + Int8 myHMBL; // Ball horizontal motion register + + bool myVDELP0; // Indicates if player 0 is being virtically delayed + bool myVDELP1; // Indicates if player 1 is being virtically delayed + bool myVDELBL; // Indicates if the ball is being virtically delayed + + bool myRESMP0; // Indicates if missle 0 is reset to player 0 + bool myRESMP1; // Indicates if missle 1 is reset to player 1 + + uInt16 myCollision; // Collision register + + // Note that these position registers contain the color clock + // on which the object's serial output should begin (0 to 159) + Int16 myPOSP0; // Player 0 position register + Int16 myPOSP1; // Player 1 position register + Int16 myPOSM0; // Missle 0 position register + Int16 myPOSM1; // Missle 1 position register + Int16 myPOSBL; // Ball position register + + private: + // Graphics for Player 0 that should be displayed. This will be + // reflected if the player is being reflected. + uInt8 myCurrentGRP0; + + // Graphics for Player 1 that should be displayed. This will be + // reflected if the player is being reflected. + uInt8 myCurrentGRP1; + + // It's VERY important that the BL, M0, M1, P0 and P1 current + // mask pointers are always on a uInt32 boundary. Otherwise, + // the TIA code will fail on a good number of CPUs. + + // Pointer to the currently active mask array for the ball + uInt8* myCurrentBLMask; + + // Pointer to the currently active mask array for missle 0 + uInt8* myCurrentM0Mask; + + // Pointer to the currently active mask array for missle 1 + uInt8* myCurrentM1Mask; + + // Pointer to the currently active mask array for player 0 + uInt8* myCurrentP0Mask; + + // Pointer to the currently active mask array for player 1 + uInt8* myCurrentP1Mask; + + // Pointer to the currently active mask array for the playfield + uInt32* myCurrentPFMask; + + private: + // Indicates when the dump for paddles was last set + Int32 myDumpDisabledCycle; + + // Indicates if the dump is current enabled for the paddles + bool myDumpEnabled; + + private: + // Color clock when last HMOVE occured + Int32 myLastHMOVEClock; + + // Indicates if HMOVE blanks are currently enabled + bool myHMOVEBlankEnabled; + + // Indicates if we're allowing HMOVE blanks to be enabled + bool myAllowHMOVEBlanks; + + // TIA M0 "bug" used for stars in Cosmic Ark flag + bool myM0CosmicArkMotionEnabled; + + // Counter used for TIA M0 "bug" + uInt32 myM0CosmicArkCounter; + + private: + // Ball mask table (entries are true or false) + static uInt8 ourBallMaskTable[4][4][320]; + + // Used to set the collision register to the correct value + static uInt16 ourCollisionTable[64]; + + // A mask table which can be used when an object is disabled + static uInt8 ourDisabledMaskTable[640]; + + // Indicates the update delay associated with poking at a TIA address + static const Int16 ourPokeDelayTable[64]; + + // Missle mask table (entries are true or false) + static uInt8 ourMissleMaskTable[4][8][4][320]; + + // Used to convert value written in a motion register into + // its internal representation + static const Int32 ourCompleteMotionTable[76][16]; + + // Indicates if HMOVE blanks should occur for the corresponding cycle + static const bool ourHMOVEBlankEnableCycles[76]; + + // Player mask table + static uInt8 ourPlayerMaskTable[4][2][8][320]; + + // Indicates if player is being reset during delay, display or other times + static Int8 ourPlayerPositionResetWhenTable[8][160][160]; + + // Used to reflect a players graphics + static uInt8 ourPlayerReflectTable[256]; + + // Playfield mask table for reflected and non-reflected playfields + static uInt32 ourPlayfieldTable[2][160]; + + // Table of RGB values for NTSC + static const uInt32 ourNTSCPalette[256]; + + // Table of RGB values for PAL + static const uInt32 ourPALPalette[256]; + + private: + // Copy constructor isn't supported by this class so make it private + TIA(const TIA&); + + // Assignment operator isn't supported by this class so make it private + TIA& operator = (const TIA&); +}; +#endif + diff --git a/stella/src/emucore/m6502/Copyright.txt b/stella/src/emucore/m6502/Copyright.txt new file mode 100644 index 000000000..809ee94e3 --- /dev/null +++ b/stella/src/emucore/m6502/Copyright.txt @@ -0,0 +1,37 @@ +=============================================================================== + + MM MM 6666 555555 0000 2222 + MMMM MMMM 66 66 55 00 00 22 22 + MM MMM MM 66 55 00 00 22 + MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" + MM MM 66 66 55 00 00 22 + MM MM 66 66 55 55 00 00 22 + MM MM 6666 5555 0000 222222 + +=============================================================================== + License Information and Copyright Notice +=============================================================================== + +Copyright (C) 1995-2002 Bradford W. Mott + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or any later version. + +You should have received a copy of the GNU General Public License version 2 +along with this program (License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY +PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES +THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN +"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE +MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + diff --git a/stella/src/emucore/m6502/License.txt b/stella/src/emucore/m6502/License.txt new file mode 100644 index 000000000..4189933be --- /dev/null +++ b/stella/src/emucore/m6502/License.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/stella/src/emucore/m6502/src/D6502.cxx b/stella/src/emucore/m6502/src/D6502.cxx new file mode 100644 index 000000000..9cac63592 --- /dev/null +++ b/stella/src/emucore/m6502/src/D6502.cxx @@ -0,0 +1,187 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: D6502.cxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $ +//============================================================================ + +#include +#include "D6502.hxx" +#include "M6502.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +D6502::D6502(System* system) + : mySystem(system) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +D6502::~D6502() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +static uInt16 dpeek(System* system, uInt16 address) +{ + return (uInt16)system->peek(address) | + (((uInt16)system->peek(address + 1)) << 8); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 D6502::disassemble(uInt16 address, char* buffer) +{ + uInt8 opcode = mySystem->peek(address); + + switch(M6502::ourAddressingModeTable[opcode]) + { + case M6502::Absolute: + sprintf(buffer, "%s $%04X", M6502::ourInstructionMnemonicTable[opcode], + dpeek(mySystem, address + 1)); + return 3; + + case M6502::AbsoluteX: + sprintf(buffer, "%s $%04X,x", M6502::ourInstructionMnemonicTable[opcode], + dpeek(mySystem, address + 1)); + return 3; + + case M6502::AbsoluteY: + sprintf(buffer, "%s $%04X,y", M6502::ourInstructionMnemonicTable[opcode], + dpeek(mySystem, address + 1)); + return 3; + + case M6502::Immediate: + sprintf(buffer, "%s #$%02X", M6502::ourInstructionMnemonicTable[opcode], + mySystem->peek(address + 1)); + return 2; + + case M6502::Implied: + sprintf(buffer, "%s", M6502::ourInstructionMnemonicTable[opcode]); + return 1; + + case M6502::Indirect: + sprintf(buffer, "%s ($%04X)", M6502::ourInstructionMnemonicTable[opcode], + dpeek(mySystem, address + 1)); + return 3; + + case M6502::IndirectX: + sprintf(buffer, "%s ($%02X,x)", + M6502::ourInstructionMnemonicTable[opcode], + mySystem->peek(address + 1)); + return 2; + + case M6502::IndirectY: + sprintf(buffer, "%s ($%02X),y", + M6502::ourInstructionMnemonicTable[opcode], + mySystem->peek(address + 1)); + return 2; + + case M6502::Relative: + sprintf(buffer, "%s $%04X", M6502::ourInstructionMnemonicTable[opcode], + address + 2 + ((Int16)(Int8)mySystem->peek(address + 1))); + return 2; + + case M6502::Zero: + sprintf(buffer, "%s $%02X", M6502::ourInstructionMnemonicTable[opcode], + mySystem->peek(address + 1)); + return 2; + + case M6502::ZeroX: + sprintf(buffer, "%s $%02X,x", M6502::ourInstructionMnemonicTable[opcode], + mySystem->peek(address + 1)); + return 2; + + case M6502::ZeroY: + sprintf(buffer, "%s $%02X,y", M6502::ourInstructionMnemonicTable[opcode], + mySystem->peek(address + 1)); + return 2; + + default: + sprintf(buffer, "dc $%02X", opcode); + return 1; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 D6502::a() +{ + return mySystem->m6502().A; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void D6502::a(uInt8 value) +{ + mySystem->m6502().A = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt16 D6502::pc() +{ + return mySystem->m6502().PC; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void D6502::pc(uInt16 value) +{ + mySystem->m6502().PC = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 D6502::ps() +{ + return mySystem->m6502().PS(); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void D6502::ps(uInt8 value) +{ + mySystem->m6502().PS(value); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 D6502::sp() +{ + return mySystem->m6502().SP; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void D6502::sp(uInt8 value) +{ + mySystem->m6502().SP = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 D6502::x() +{ + return mySystem->m6502().X; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void D6502::x(uInt8 value) +{ + mySystem->m6502().X = value; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 D6502::y() +{ + return mySystem->m6502().Y; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void D6502::y(uInt8 value) +{ + mySystem->m6502().Y = value; +} + diff --git a/stella/src/emucore/m6502/src/D6502.hxx b/stella/src/emucore/m6502/src/D6502.hxx new file mode 100644 index 000000000..8fb6aa8f1 --- /dev/null +++ b/stella/src/emucore/m6502/src/D6502.hxx @@ -0,0 +1,152 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: D6502.hxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $ +//============================================================================ + +#ifndef D6502_HXX +#define D6502_HXX + +class D6502; +class M6502; +class System; + +#include "bspf.hxx" + +/** + This is a base class for 6502 debuggers. This class provides the + basic functionality needed for interactive debuggers. + + @author Bradford W. Mott + @version $Id: D6502.hxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $ +*/ +class D6502 +{ + public: + /** + Create a new 6502 debugger for the specified system + + @param system The system the debugger should operate on + */ + D6502(System* system); + + /** + Destructor + */ + virtual ~D6502(); + + public: + /** + Disassemble a single instruction at the specified address into + the given buffer and answer the number of bytes disassembled. + The buffer should be at least 20 characters long. + + @param address The address to disassemble code at + @param buffer The buffer where the ASCII disassemble should be stored + @return The number of bytes disassembled + */ + uInt16 disassemble(uInt16 address, char* buffer); + + public: + /** + Get the value of the accumulator + + @return The accumulator's value + */ + uInt8 a(); + + /** + Change value of the accumulator + + @param value The value to set the accumulator to + */ + void a(uInt8 value); + + /** + Get value of the program counter + + @return The program counter's value + */ + uInt16 pc(); + + /** + Change value of the program counter + + @param value The value to set the program counter to + */ + void pc(uInt16 value); + + /** + Get the value of the processor status register + + @return The processor status register's value + */ + uInt8 ps(); + + /** + Change value of the processor status register + + @param value The value to set the processor status register to + */ + void ps(uInt8 value); + + /** + Get the value of the stack pointer + + @return The stack pointer's value + */ + uInt8 sp(); + + /** + Change value of the stack pointer + + @param value The value to set the stack pointer to + */ + void sp(uInt8 value); + + /** + Get the value of the X index register + + @return The X register's value + */ + uInt8 x(); + + /** + Change value of the X index register + + @param value The value to set the X register to + */ + void x(uInt8 value); + + /** + Get the value of the Y index register + + @return The Y register's value + */ + uInt8 y(); + + /** + Change value of the Y index register + + @param value The value to set the Y register to + */ + void y(uInt8 value); + + protected: + // Pointer to the system I'm debugging + System* mySystem; +}; +#endif + diff --git a/stella/src/emucore/m6502/src/Device.cxx b/stella/src/emucore/m6502/src/Device.cxx new file mode 100644 index 000000000..b0d6c1832 --- /dev/null +++ b/stella/src/emucore/m6502/src/Device.cxx @@ -0,0 +1,37 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Device.cxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $ +//============================================================================ + +#include "Device.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Device::Device() + : mySystem(0) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +Device::~Device() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void Device::systemCyclesReset() +{ + // By default I do nothing when my system resets its cycle counter +} + diff --git a/stella/src/emucore/m6502/src/Device.hxx b/stella/src/emucore/m6502/src/Device.hxx new file mode 100644 index 000000000..1a58acde8 --- /dev/null +++ b/stella/src/emucore/m6502/src/Device.hxx @@ -0,0 +1,95 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: Device.hxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $ +//============================================================================ + +#ifndef DEVICE_HXX +#define DEVICE_HXX + +class System; + +#include "bspf.hxx" + +/** + Abstract base class for devices which can be attached to a 6502 + based system. + + @author Bradford W. Mott + @version $Id: Device.hxx,v 1.1.1.1 2001-12-27 19:54:29 bwmott Exp $ +*/ +class Device +{ + public: + /** + Create a new device + */ + Device(); + + /** + Destructor + */ + virtual ~Device(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const = 0; + + /** + Reset device to its power-on state + */ + virtual void reset() = 0; + + /** + Notification method invoked by the system right before the + system resets its cycle counter to zero. It may be necessary + to override this method for devices that remember cycle counts. + */ + virtual void systemCyclesReset(); + + /** + Install device in the specified system. Invoked by the system + when the device is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system) = 0; + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address) = 0; + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value) = 0; + + protected: + /// Pointer to the system the device is installed in or the null pointer + System* mySystem; +}; +#endif + diff --git a/stella/src/emucore/m6502/src/M6502.cxx b/stella/src/emucore/m6502/src/M6502.cxx new file mode 100644 index 000000000..b4fafbfdc --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502.cxx @@ -0,0 +1,335 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502.cxx,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +//============================================================================ + +#include "M6502.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502::M6502(uInt32 systemCyclesPerProcessorCycle) + : myExecutionStatus(0), + mySystem(0), + mySystemCyclesPerProcessorCycle(systemCyclesPerProcessorCycle) +{ + uInt16 t; + + // Compute the BCD lookup table + for(t = 0; t < 256; ++t) + { + ourBCDTable[0][t] = ((t >> 4) * 10) + (t & 0x0f); + ourBCDTable[1][t] = (((t % 100) / 10) << 4) | (t % 10); + } + + // Compute the System Cycle table + for(t = 0; t < 256; ++t) + { + myInstructionSystemCycleTable[t] = ourInstructionProcessorCycleTable[t] * + mySystemCyclesPerProcessorCycle; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502::~M6502() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502::install(System& system) +{ + // Remember which system I'm installed in + mySystem = &system; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502::reset() +{ + // Clear the execution status flags + myExecutionStatus = 0; + + // Set registers to default values + A = X = Y = 0; + SP = 0xff; + PS(0x20); + + // Load PC from the reset vector + PC = (uInt16)mySystem->peek(0xfffc) | ((uInt16)mySystem->peek(0xfffd) << 8); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502::irq() +{ + myExecutionStatus |= MaskableInterruptBit; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502::nmi() +{ + myExecutionStatus |= NonmaskableInterruptBit; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502::stop() +{ + myExecutionStatus |= StopExecutionBit; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502::AddressingMode M6502::addressingMode(uInt8 opcode) const +{ + return ourAddressingModeTable[opcode]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 M6502::PS() const +{ + uInt8 ps = 0x20; + + if(N) + ps |= 0x80; + if(V) + ps |= 0x40; + if(B) + ps |= 0x10; + if(D) + ps |= 0x08; + if(I) + ps |= 0x04; + if(!notZ) + ps |= 0x02; + if(C) + ps |= 0x01; + + return ps; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502::PS(uInt8 ps) +{ + N = ps & 0x80; + V = ps & 0x40; + B = ps & 0x10; + D = ps & 0x08; + I = ps & 0x04; + notZ = !(ps & 0x02); + C = ps & 0x01; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +ostream& operator<<(ostream& out, const M6502::AddressingMode& mode) +{ + switch(mode) + { + case M6502::Absolute: + out << "$nnnn "; + break; + case M6502::AbsoluteX: + out << "$nnnn,X"; + break; + case M6502::AbsoluteY: + out << "$nnnn,Y"; + break; + case M6502::Implied: + out << "implied"; + break; + case M6502::Immediate: + out << "#$nn "; + break; + case M6502::Indirect: + out << "($nnnn)"; + break; + case M6502::IndirectX: + out << "($nn,X)"; + break; + case M6502::IndirectY: + out << "($nn),Y"; + break; + case M6502::Invalid: + out << "invalid"; + break; + case M6502::Relative: + out << "$nn "; + break; + case M6502::Zero: + out << "$nn "; + break; + case M6502::ZeroX: + out << "$nn,X "; + break; + case M6502::ZeroY: + out << "$nn,Y "; + break; + } + return out; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 M6502::ourBCDTable[2][256]; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502::AddressingMode M6502::ourAddressingModeTable[256] = { + Implied, IndirectX, Invalid, IndirectX, // 0x0? + Zero, Zero, Zero, Zero, + Implied, Immediate, Implied, Invalid, + Absolute, Absolute, Absolute, Absolute, + + Relative, IndirectY, Invalid, IndirectY, // 0x1? + ZeroX, ZeroX, ZeroX, ZeroX, + Implied, AbsoluteY, Implied, AbsoluteY, + AbsoluteX, AbsoluteX, AbsoluteX, AbsoluteX, + + Absolute, IndirectX, Invalid, IndirectX, // 0x2? + Zero, Zero, Zero, Zero, + Implied, Immediate, Implied, Invalid, + Absolute, Absolute, Absolute, Absolute, + + Relative, IndirectY, Invalid, IndirectY, // 0x3? + ZeroX, ZeroX, ZeroX, ZeroX, + Implied, AbsoluteY, Implied, AbsoluteY, + AbsoluteX, AbsoluteX, AbsoluteX, AbsoluteX, + + Implied, IndirectX, Invalid, Invalid, // 0x4? + Zero, Zero, Zero, Invalid, + Implied, Immediate, Implied, Invalid, + Absolute, Absolute, Absolute, Invalid, + + Relative, IndirectY, Invalid, Invalid, // 0x5? + ZeroX, ZeroX, ZeroX, Invalid, + Implied, AbsoluteY, Implied, Invalid, + AbsoluteX, AbsoluteX, AbsoluteX, Invalid, + + Implied, IndirectX, Invalid, Invalid, // 0x6? + Zero, Zero, Zero, Invalid, + Implied, Immediate, Implied, Invalid, + Indirect, Absolute, Absolute, Invalid, + + Relative, IndirectY, Invalid, Invalid, // 0x7? + ZeroX, ZeroX, ZeroX, Invalid, + Implied, AbsoluteY, Implied, Invalid, + AbsoluteX, AbsoluteX, AbsoluteX, Invalid, + + Immediate, IndirectX, Immediate, IndirectX, // 0x8? + Zero, Zero, Zero, Zero, + Implied, Invalid, Implied, Invalid, + Absolute, Absolute, Absolute, Absolute, + + Relative, IndirectY, Invalid, Invalid, // 0x9? + ZeroX, ZeroX, ZeroY, ZeroY, + Implied, AbsoluteY, Implied, Invalid, + Invalid, AbsoluteX, Invalid, Invalid, + + Immediate, IndirectX, Immediate, Invalid, // 0xA? + Zero, Zero, Zero, Invalid, + Implied, Immediate, Implied, Invalid, + Absolute, Absolute, Absolute, Invalid, + + Relative, IndirectY, Invalid, Invalid, // 0xB? + ZeroX, ZeroX, ZeroY, Invalid, + Implied, AbsoluteY, Implied, Invalid, + AbsoluteX, AbsoluteX, AbsoluteY, Invalid, + + Immediate, IndirectX, Immediate, Invalid, // 0xC? + Zero, Zero, Zero, Invalid, + Implied, Immediate, Implied, Invalid, + Absolute, Absolute, Absolute, Invalid, + + Relative, IndirectY, Invalid, Invalid, // 0xD? + ZeroX, ZeroX, ZeroX, Invalid, + Implied, AbsoluteY, Implied, Invalid, + AbsoluteX, AbsoluteX, AbsoluteX, Invalid, + + Immediate, IndirectX, Immediate, Invalid, // 0xE? + Zero, Zero, Zero, Invalid, + Implied, Immediate, Implied, Invalid, + Absolute, Absolute, Absolute, Invalid, + + Relative, IndirectY, Invalid, Invalid, // 0xF? + ZeroX, ZeroX, ZeroX, Invalid, + Implied, AbsoluteY, Implied, Invalid, + AbsoluteX, AbsoluteX, AbsoluteX, Invalid + }; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt32 M6502::ourInstructionProcessorCycleTable[256] = { +// 0 1 2 3 4 5 6 7 8 9 a b c d e f + 7, 6, 2, 8, 3, 3, 5, 5, 3, 2, 2, 2, 4, 4, 6, 6, // 0 + 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 1 + 6, 6, 2, 8, 3, 3, 5, 5, 4, 2, 2, 2, 4, 4, 6, 6, // 2 + 2, 5, 2, 8, 4, 4, 6, 6, 2, 4, 2, 7, 4, 4, 7, 7, // 3 + 6, 6, 2, 2, 3, 3, 5, 2, 3, 2, 2, 2, 3, 4, 6, 2, // 4 + 2, 5, 2, 2, 4, 4, 6, 2, 2, 4, 2, 2, 4, 4, 7, 2, // 5 + 6, 6, 2, 2, 3, 3, 5, 2, 4, 2, 2, 2, 5, 4, 6, 2, // 6 + 2, 5, 2, 2, 4, 4, 6, 2, 2, 4, 2, 2, 4, 4, 7, 2, // 7 + 2, 6, 2, 6, 3, 3, 3, 3, 2, 2, 2, 2, 4, 4, 4, 4, // 8 + 2, 6, 2, 2, 4, 4, 4, 4, 2, 5, 2, 2, 2, 5, 2, 2, // 9 + 2, 6, 2, 2, 3, 3, 3, 2, 2, 2, 2, 2, 4, 4, 4, 2, // a + 2, 5, 2, 2, 4, 4, 4, 2, 2, 4, 2, 2, 4, 4, 4, 2, // b + 2, 6, 2, 2, 3, 3, 5, 2, 2, 2, 2, 2, 4, 4, 6, 2, // c + 2, 5, 2, 2, 4, 4, 6, 2, 2, 4, 2, 2, 4, 4, 7, 2, // d + 2, 6, 2, 2, 3, 3, 5, 2, 2, 2, 2, 2, 4, 4, 6, 2, // e + 2, 5, 2, 2, 4, 4, 6, 2, 2, 4, 2, 2, 4, 4, 7, 2 // f + }; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* M6502::ourInstructionMnemonicTable[256] = { + "BRK", "ORA", "n/a", "aso", "nop", "ORA", "ASL", "aso", // 0x0? + "PHP", "ORA", "ASLA", "n/a", "nop", "ORA", "ASL", "aso", + + "BPL", "ORA", "n/a", "aso", "nop", "ORA", "ASL", "aso", // 0x1? + "CLC", "ORA", "nop", "aso", "nop", "ORA", "ASL", "aso", + + "JSR", "AND", "n/a", "rla", "BIT", "AND", "ROL", "rla", // 0x2? + "PLP", "AND", "ROLA", "n/a", "BIT", "AND", "ROL", "rla", + + "BMI", "AND", "rla", "n/a", "nop", "AND", "ROL", "rla", // 0x3? + "SEC", "AND", "nop", "rla", "nop", "AND", "ROL", "rla", + + "RTI", "EOR", "n/a", "n/a", "nop", "EOR", "LSR", "n/a", // 0x4? + "PHA", "EOR", "LSRA", "n/a", "JMP", "EOR", "LSR", "n/a", + + "BVC", "EOR", "n/a", "n/a", "nop", "EOR", "LSR", "n/a", // 0x5? + "CLI", "EOR", "nop", "n/a", "nop", "EOR", "LSR", "n/a", + + "RTS", "ADC", "n/a", "n/a", "nop", "ADC", "ROR", "n/a", // 0x6? + "PLA", "ADC", "RORA", "n/a", "JMP", "ADC", "ROR", "n/a", + + "BVS", "ADC", "n/a", "n/a", "nop", "ADC", "ROR", "n/a", // 0x7? + "SEI", "ADC", "nop", "n/a", "nop", "ADC", "ROR", "n/a", + + "nop", "STA", "nop", "axs", "STY", "STA", "STX", "axs", // 0x8? + "DEY", "n/a", "TXA", "n/a", "STY", "STA", "STX", "axs", + + "BCC", "STA", "n/a", "n/a", "STY", "STA", "STX", "axs", // 0x9? + "TYA", "STA", "TXS", "n/a", "n/a", "STA", "n/a", "n/a", + + "LDY", "LDA", "LDX", "n/a", "LDY", "LDA", "LDX", "n/a", // 0xA? + "TAY", "LDA", "TAX", "n/a", "LDY", "LDA", "LDX", "n/a", + + "BCS", "LDA", "n/a", "n/a", "LDY", "LDA", "LDX", "n/a", // 0xB? + "CLV", "LDA", "TSX", "n/a", "LDY", "LDA", "LDX", "n/a", + + "CPY", "CMP", "nop", "n/a", "CPY", "CMP", "DEC", "n/a", // 0xC? + "INY", "CMP", "DEX", "n/a", "CPY", "CMP", "DEC", "n/a", + + "BNE", "CMP", "n/a", "n/a", "nop", "CMP", "DEC", "n/a", // 0xD? + "CLD", "CMP", "nop", "n/a", "nop", "CMP", "DEC", "n/a", + + "CPX", "SBC", "nop", "n/a", "CPX", "SBC", "INC", "n/a", // 0xE? + "INX", "SBC", "NOP", "n/a", "CPX", "SBC", "INC", "n/a", + + "BEQ", "SBC", "n/a", "n/a", "nop", "SBC", "INC", "n/a", // 0xF? + "SED", "SBC", "nop", "n/a", "nop", "SBC", "INC", "n/a" +}; + diff --git a/stella/src/emucore/m6502/src/M6502.hxx b/stella/src/emucore/m6502/src/M6502.hxx new file mode 100644 index 000000000..3936a365c --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502.hxx @@ -0,0 +1,219 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502.hxx,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +//============================================================================ + +#ifndef M6502_HXX +#define M6502_HXX + +class D6502; +class M6502; + +#include "bspf.hxx" +#include "System.hxx" + +/** + This is an abstract base class for classes that emulate the + 6502 microprocessor. The 6502 is an 8-bit microprocessor that + has a 64K addressing space. + + @author Bradford W. Mott + @version $Id: M6502.hxx,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +*/ +class M6502 +{ + public: + /** + The 6502 debugger class is a friend who needs special access + */ + friend class D6502; + + public: + /** + Enumeration of the 6502 addressing modes + */ + enum AddressingMode + { + Absolute, AbsoluteX, AbsoluteY, Immediate, Implied, + Indirect, IndirectX, IndirectY, Invalid, Relative, + Zero, ZeroX, ZeroY + }; + + public: + /** + Create a new 6502 microprocessor with the specified cycle + multiplier. The cycle multiplier is the number of system cycles + per processor cycle. + + @param systemCyclesPerProcessorCycle The cycle multiplier + */ + M6502(uInt32 systemCyclesPerProcessorCycle); + + /** + Destructor + */ + virtual ~M6502(); + + public: + /** + Install the processor in the specified system. Invoked by the + system when the processor is attached to it. + + @param system The system the processor should install itself in + */ + virtual void install(System& system); + + public: + /** + Reset the processor to its power-on state. This method should not + be invoked until the entire 6502 system is constructed and installed + since it involves reading the reset vector from memory. + */ + virtual void reset(); + + /** + Request a maskable interrupt + */ + virtual void irq(); + + /** + Request a non-maskable interrupt + */ + virtual void nmi(); + + public: + /** + Get the addressing mode of the specified instruction + + @param opcode The opcode of the instruction + @return The addressing mode of the instruction + */ + AddressingMode addressingMode(uInt8 opcode) const; + + public: + /** + Execute instructions until the specified number of instructions + is executed, someone stops execution, or an error occurs. Answers + true iff execution stops normally. + + @param number Indicates the number of instructions to execute + @return true iff execution stops normally + */ + virtual bool execute(uInt32 number) = 0; + + /** + Tell the processor to stop executing instructions. Invoking this + method while the processor is executing instructions will stop + execution as soon as possible. + */ + void stop(); + + /** + Answer true iff a fatal error has occured from which the processor + cannot recover (i.e. illegal instruction, etc.) + + @return true iff a fatal error has occured + */ + bool fatalError() const + { + return myExecutionStatus & FatalErrorBit; + } + + public: + /** + Overload the ostream output operator for addressing modes. + + @param out The stream to output the addressing mode to + @param mode The addressing mode to output + */ + friend ostream& operator<<(ostream& out, const AddressingMode& mode); + + protected: + /** + Get the 8-bit value of the Processor Status register. + + @return The processor status register + */ + uInt8 PS() const; + + /** + Change the Processor Status register to correspond to the given value. + + @param ps The value to set the processor status register to + */ + void PS(uInt8 ps); + + protected: + uInt8 A; // Accumulator + uInt8 X; // X index register + uInt8 Y; // Y index register + uInt8 SP; // Stack Pointer + uInt8 IR; // Instruction register + uInt16 PC; // Program Counter + + bool N; // N flag for processor status register + bool V; // V flag for processor status register + bool B; // B flag for processor status register + bool D; // D flag for processor status register + bool I; // I flag for processor status register + bool notZ; // Z flag complement for processor status register + bool C; // C flag for processor status register + + /** + Bit fields used to indicate that certain conditions need to be + handled such as stopping execution, fatal errors, maskable interrupts + and non-maskable interrupts + */ + uInt8 myExecutionStatus; + + /** + Constants used for setting bits in myExecutionStatus + */ + enum + { + StopExecutionBit = 0x01, + FatalErrorBit = 0x02, + MaskableInterruptBit = 0x04, + NonmaskableInterruptBit = 0x08 + }; + + /// Pointer to the system the processor is installed in or the null pointer + System* mySystem; + + /// Indicates the number of system cycles per processor cycle + const uInt32 mySystemCyclesPerProcessorCycle; + + /// Table of system cycles for each instruction + uInt32 myInstructionSystemCycleTable[256]; + + protected: + /// Addressing mode for each of the 256 opcodes + static AddressingMode ourAddressingModeTable[256]; + + /// Lookup table used for binary-code-decimal math + static uInt8 ourBCDTable[2][256]; + + /** + Table of instruction processor cycle times. In some cases additional + cycles will be added during the execution of an instruction. + */ + static uInt32 ourInstructionProcessorCycleTable[256]; + + /// Table of instruction mnemonics + static const char* ourInstructionMnemonicTable[256]; +}; +#endif + diff --git a/stella/src/emucore/m6502/src/M6502.m4 b/stella/src/emucore/m6502/src/M6502.m4 new file mode 100644 index 000000000..6124c1be5 --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502.m4 @@ -0,0 +1,1410 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502.m4,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +//============================================================================ + +/** + Code and cases to emulate each of the 6502 instruction + + @author Bradford W. Mott + @version $Id: M6502.m4,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + +define(M6502_ADC, `{ + uInt8 oldA = A; + + if(!D) + { + Int16 sum = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((sum > 127) || (sum < -128)); + + sum = (Int16)A + (Int16)operand + (C ? 1 : 0); + A = sum; + C = (sum > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 sum = ourBCDTable[0][A] + ourBCDTable[0][operand] + (C ? 1 : 0); + + C = (sum > 99); + A = ourBCDTable[1][sum & 0xff]; + notZ = A; + N = A & 0x80; + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +}') + +define(M6502_AND, `{ + A &= operand; + notZ = A; + N = A & 0x80; +}') + +define(M6502_ASL, `{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +}') + +define(M6502_ASLA, `{ + // Set carry flag according to the left-most bit in A + C = A & 0x80; + + A <<= 1; + + notZ = A; + N = A & 0x80; +}') + +define(M6502_ASO, `{ + // Set carry flag according to the left-most bit in value + C = operand & 0x80; + + operand <<= 1; + poke(operandAddress, operand); + + A |= operand; + notZ = A; + N = A & 0x80; +}') + +define(M6502_AXS, `{ + poke(operandAddress, A & X); +}') + + +define(M6502_BIT, `{ + notZ = (A & operand); + N = operand & 0x80; + V = operand & 0x40; +}') + +define(M6502_BRK, `{ + peek(PC++); + poke(0x0100 + SP--, PC >> 8); + poke(0x0100 + SP--, PC & 0x00ff); + poke(0x0100 + SP--, PS()); + + B = true; + I = true; + + PC = peek(0xfffe); + PC |= ((uInt16)peek(0xffff) << 8); +}') + +define(M6502_CLC, `{ + C = false; +}') + +define(M6502_CLD, `{ + D = false; +}') + +define(M6502_CLI, `{ + I = false; +}') + +define(M6502_CLV, `{ + V = false; +}') + +define(M6502_CMP, `{ + uInt16 value = (uInt16)A - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +}') + +define(M6502_CPX, `{ + uInt16 value = (uInt16)X - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +}') + +define(M6502_CPY, `{ + uInt16 value = (uInt16)Y - (uInt16)operand; + + notZ = value; + N = value & 0x0080; + C = !(value & 0x0100); +}') + +define(M6502_DEC, `{ + uInt8 value = operand - 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +}') + + +define(M6502_DEX, `{ + X--; + + notZ = X; + N = X & 0x80; +}') + + +define(M6502_DEY, `{ + Y--; + + notZ = Y; + N = Y & 0x80; +}') + +define(M6502_EOR, `{ + A ^= operand; + notZ = A; + N = A & 0x80; +}') + +define(M6502_INC, `{ + uInt8 value = operand + 1; + poke(operandAddress, value); + + notZ = value; + N = value & 0x80; +}') + +define(M6502_INX, `{ + X++; + notZ = X; + N = X & 0x80; +}') + +define(M6502_INY, `{ + Y++; + notZ = Y; + N = Y & 0x80; +}') + +define(M6502_JMP, `{ + PC = operandAddress; +}') + +define(M6502_JSR, `{ + uInt8 low = peek(PC++); + peek(0x0100 + SP); + + // It seems that the 650x does not push the address of the next instruction + // on the stack it actually pushes the address of the next instruction + // minus one. This is compensated for in the RTS instruction + poke(0x0100 + SP--, PC >> 8); + poke(0x0100 + SP--, PC & 0xff); + + PC = low | ((uInt16)peek(PC++) << 8); +}') + +define(M6502_LDA, `{ + A = operand; + notZ = A; + N = A & 0x80; +}') + +define(M6502_LDX, `{ + X = operand; + notZ = X; + N = X & 0x80; +}') + +define(M6502_LDY, `{ + Y = operand; + notZ = Y; + N = Y & 0x80; +}') + +define(M6502_LSR, `{ + // Set carry flag according to the right-most bit in value + C = operand & 0x01; + + operand = (operand >> 1) & 0x7f; + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +}') + +define(M6502_LSRA, `{ + // Set carry flag according to the right-most bit + C = A & 0x01; + + A = (A >> 1) & 0x7f; + + notZ = A; + N = A & 0x80; +}') + +define(M6502_NOP, `{ +}') + +define(M6502_ORA, `{ + A |= operand; + notZ = A; + N = A & 0x80; +}') + +define(M6502_PHA, `{ + poke(0x0100 + SP--, A); +}') + +define(M6502_PHP, `{ + poke(0x0100 + SP--, PS()); +}') + +define(M6502_PLA, `{ + peek(0x0100 + SP++); + A = peek(0x0100 + SP); + notZ = A; + N = A & 0x80; +}') + +define(M6502_PLP, `{ + peek(0x0100 + SP++); + PS(peek(0x0100 + SP)); +}') + +define(M6502_RLA, `{ + uInt8 value = (operand << 1) | (C ? 1 : 0); + poke(operandAddress, value); + + A &= value; + C = operand & 0x80; + notZ = A; + N = A & 0x80; +}') + +define(M6502_ROL, `{ + bool oldC = C; + + // Set carry flag according to the left-most bit in operand + C = operand & 0x80; + + operand = (operand << 1) | (oldC ? 1 : 0); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +}') + +define(M6502_ROLA, `{ + bool oldC = C; + + // Set carry flag according to the left-most bit + C = A & 0x80; + + A = (A << 1) | (oldC ? 1 : 0); + + notZ = A; + N = A & 0x80; +}') + +define(M6502_ROR, `{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = operand & 0x01; + + operand = ((operand >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + poke(operandAddress, operand); + + notZ = operand; + N = operand & 0x80; +}') + +define(M6502_RORA, `{ + bool oldC = C; + + // Set carry flag according to the right-most bit + C = A & 0x01; + + A = ((A >> 1) & 0x7f) | (oldC ? 0x80 : 0x00); + + notZ = A; + N = A & 0x80; +}') + + +define(M6502_RTI, `{ + peek(0x0100 + SP++); + PS(peek(0x0100 + SP++)); + PC = peek(0x0100 + SP++); + PC |= ((uInt16)peek(0x0100 + SP) << 8); +}') + +define(M6502_RTS, `{ + peek(0x0100 + SP++); + PC = peek(0x0100 + SP++); + PC |= ((uInt16)peek(0x0100 + SP) << 8); + peek(PC++); +}') + +define(M6502_SBC, `{ + uInt8 oldA = A; + + if(!D) + { + operand = ~operand; + Int16 difference = (Int16)((Int8)A) + (Int16)((Int8)operand) + (C ? 1 : 0); + V = ((difference > 127) || (difference < -128)); + + difference = ((Int16)A) + ((Int16)operand) + (C ? 1 : 0); + A = difference; + C = (difference > 0xff); + notZ = A; + N = A & 0x80; + } + else + { + Int16 difference = ourBCDTable[0][A] - ourBCDTable[0][operand] + - (C ? 0 : 1); + + if(difference < 0) + difference += 100; + + A = ourBCDTable[1][difference]; + notZ = A; + N = A & 0x80; + + C = (oldA >= (operand + (C ? 0 : 1))); + V = ((oldA ^ A) & 0x80) && ((A ^ operand) & 0x80); + } +}') + +define(M6502_SEC, `{ + C = true; +}') + +define(M6502_SED, `{ + D = true; +}') + +define(M6502_SEI, `{ + I = true; +}') + +define(M6502_STA, `{ + poke(operandAddress, A); +}') + +define(M6502_STX, `{ + poke(operandAddress, X); +}') + +define(M6502_STY, `{ + poke(operandAddress, Y); +}') + +define(M6502_TAX, `{ + X = A; + notZ = X; + N = X & 0x80; +}') + +define(M6502_TAY, `{ + Y = A; + notZ = Y; + N = Y & 0x80; +}') + +define(M6502_TSX, `{ + X = SP; + notZ = X; + N = X & 0x80; +}') + +define(M6502_TXA, `{ + A = X; + notZ = A; + N = A & 0x80; +}') + +define(M6502_TXS, `{ + SP = X; +}') + +define(M6502_TYA, `{ + A = Y; + notZ = A; + N = A & 0x80; +}') + + +case 0x69: +M6502_IMMEDIATE_READ +M6502_ADC +break; + +case 0x65: +M6502_ZERO_READ +M6502_ADC +break; + +case 0x75: +M6502_ZEROX_READ +M6502_ADC +break; + +case 0x6D: +M6502_ABSOLUTE_READ +M6502_ADC +break; + +case 0x7D: +M6502_ABSOLUTEX_READ +M6502_ADC +break; + +case 0x79: +M6502_ABSOLUTEY_READ +M6502_ADC +break; + +case 0x61: +M6502_INDIRECTX_READ +M6502_ADC +break; + +case 0x71: +M6502_INDIRECTY_READ +M6502_ADC +break; + + + +case 0x29: +M6502_IMMEDIATE_READ +M6502_AND +break; + +case 0x25: +M6502_ZERO_READ +M6502_AND +break; + +case 0x35: +M6502_ZEROX_READ +M6502_AND +break; + +case 0x2D: +M6502_ABSOLUTE_READ +M6502_AND +break; + +case 0x3D: +M6502_ABSOLUTEX_READ +M6502_AND +break; + +case 0x39: +M6502_ABSOLUTEY_READ +M6502_AND +break; + +case 0x21: +M6502_INDIRECTX_READ +M6502_AND +break; + +case 0x31: +M6502_INDIRECTY_READ +M6502_AND +break; + + +case 0x0a: +M6502_IMPLIED +M6502_ASLA +break; + +case 0x06: +M6502_ZERO_READMODIFYWRITE +M6502_ASL +break; + +case 0x16: +M6502_ZEROX_READMODIFYWRITE +M6502_ASL +break; + +case 0x0e: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_ASL +break; + +case 0x1e: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_ASL +break; + + +case 0x0f: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_ASO +break; + +case 0x1f: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_ASO +break; + +case 0x1b: +M6502_ABSOLUTEY_READMODIFYWRITE +M6502_ASO +break; + +case 0x07: +M6502_ZERO_READMODIFYWRITE +M6502_ASO +break; + +case 0x17: +M6502_ZEROX_READMODIFYWRITE +M6502_ASO +break; + +case 0x03: +M6502_INDIRECTX_READMODIFYWRITE +M6502_ASO +break; + +case 0x13: +M6502_INDIRECTY_READMODIFYWRITE +M6502_ASO +break; + + +case 0x8f: +M6502_ABSOLUTE_WRITE +M6502_AXS +break; + +case 0x87: +M6502_ZERO_WRITE +M6502_AXS +break; + +case 0x97: +M6502_ZEROY_WRITE +M6502_AXS +break; + +case 0x83: +M6502_INDIRECTX_WRITE +M6502_AXS +break; + + +case 0x90: +M6502_IMMEDIATE_READ +M6502_BCC +break; + + +case 0xb0: +M6502_IMMEDIATE_READ +M6502_BCS +break; + + +case 0xf0: +M6502_IMMEDIATE_READ +M6502_BEQ +break; + + +case 0x24: +M6502_ZERO_READ +M6502_BIT +break; + +case 0x2C: +M6502_ABSOLUTE_READ +M6502_BIT +break; + + +case 0x30: +M6502_IMMEDIATE_READ +M6502_BMI +break; + + +case 0xD0: +M6502_IMMEDIATE_READ +M6502_BNE +break; + + +case 0x10: +M6502_IMMEDIATE_READ +M6502_BPL +break; + + +case 0x00: +M6502_BRK +break; + + +case 0x50: +M6502_IMMEDIATE_READ +M6502_BVC +break; + + +case 0x70: +M6502_IMMEDIATE_READ +M6502_BVS +break; + + +case 0x18: +M6502_IMPLIED +M6502_CLC +break; + + +case 0xd8: +M6502_IMPLIED +M6502_CLD +break; + + +case 0x58: +M6502_IMPLIED +M6502_CLI +break; + + +case 0xb8: +M6502_IMPLIED +M6502_CLV +break; + + +case 0xc9: +M6502_IMMEDIATE_READ +M6502_CMP +break; + +case 0xc5: +M6502_ZERO_READ +M6502_CMP +break; + +case 0xd5: +M6502_ZEROX_READ +M6502_CMP +break; + +case 0xcd: +M6502_ABSOLUTE_READ +M6502_CMP +break; + +case 0xdd: +M6502_ABSOLUTEX_READ +M6502_CMP +break; + +case 0xd9: +M6502_ABSOLUTEY_READ +M6502_CMP +break; + +case 0xc1: +M6502_INDIRECTX_READ +M6502_CMP +break; + +case 0xd1: +M6502_INDIRECTY_READ +M6502_CMP +break; + + +case 0xe0: +M6502_IMMEDIATE_READ +M6502_CPX +break; + +case 0xe4: +M6502_ZERO_READ +M6502_CPX +break; + +case 0xec: +M6502_ABSOLUTE_READ +M6502_CPX +break; + + +case 0xc0: +M6502_IMMEDIATE_READ +M6502_CPY +break; + +case 0xc4: +M6502_ZERO_READ +M6502_CPY +break; + +case 0xcc: +M6502_ABSOLUTE_READ +M6502_CPY +break; + + +case 0xc6: +M6502_ZERO_READMODIFYWRITE +M6502_DEC +break; + +case 0xd6: +M6502_ZEROX_READMODIFYWRITE +M6502_DEC +break; + +case 0xce: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_DEC +break; + +case 0xde: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_DEC +break; + + +case 0xca: +M6502_IMPLIED +M6502_DEX +break; + + +case 0x88: +M6502_IMPLIED +M6502_DEY +break; + + +case 0x49: +M6502_IMMEDIATE_READ +M6502_EOR +break; + +case 0x45: +M6502_ZERO_READ +M6502_EOR +break; + +case 0x55: +M6502_ZEROX_READ +M6502_EOR +break; + +case 0x4d: +M6502_ABSOLUTE_READ +M6502_EOR +break; + +case 0x5d: +M6502_ABSOLUTEX_READ +M6502_EOR +break; + +case 0x59: +M6502_ABSOLUTEY_READ +M6502_EOR +break; + +case 0x41: +M6502_INDIRECTX_READ +M6502_EOR +break; + +case 0x51: +M6502_INDIRECTY_READ +M6502_EOR +break; + + +case 0xe6: +M6502_ZERO_READMODIFYWRITE +M6502_INC +break; + +case 0xf6: +M6502_ZEROX_READMODIFYWRITE +M6502_INC +break; + +case 0xee: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_INC +break; + +case 0xfe: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_INC +break; + + +case 0xe8: +M6502_IMPLIED +M6502_INX +break; + + +case 0xc8: +M6502_IMPLIED +M6502_INY +break; + + +case 0x4c: +M6502_ABSOLUTE_WRITE +M6502_JMP +break; + +case 0x6c: +M6502_INDIRECT +M6502_JMP +break; + + +case 0x20: +M6502_JSR +break; + + +case 0xa9: +M6502_IMMEDIATE_READ +M6502_LDA +break; + +case 0xa5: +M6502_ZERO_READ +M6502_LDA +break; + +case 0xb5: +M6502_ZEROX_READ +M6502_LDA +break; + +case 0xad: +M6502_ABSOLUTE_READ +M6502_LDA +break; + +case 0xbd: +M6502_ABSOLUTEX_READ +M6502_LDA +break; + +case 0xb9: +M6502_ABSOLUTEY_READ +M6502_LDA +break; + +case 0xa1: +M6502_INDIRECTX_READ +M6502_LDA +break; + +case 0xb1: +M6502_INDIRECTY_READ +M6502_LDA +break; + + +case 0xa2: +M6502_IMMEDIATE_READ +M6502_LDX +break; + +case 0xa6: +M6502_ZERO_READ +M6502_LDX +break; + +case 0xb6: +M6502_ZEROY_READ +M6502_LDX +break; + +case 0xae: +M6502_ABSOLUTE_READ +M6502_LDX +break; + +case 0xbe: +M6502_ABSOLUTEY_READ +M6502_LDX +break; + + +case 0xa0: +M6502_IMMEDIATE_READ +M6502_LDY +break; + +case 0xa4: +M6502_ZERO_READ +M6502_LDY +break; + +case 0xb4: +M6502_ZEROX_READ +M6502_LDY +break; + +case 0xac: +M6502_ABSOLUTE_READ +M6502_LDY +break; + +case 0xbc: +M6502_ABSOLUTEX_READ +M6502_LDY +break; + + +case 0x4a: +M6502_IMPLIED +M6502_LSRA +break; + + +case 0x46: +M6502_ZERO_READMODIFYWRITE +M6502_LSR +break; + +case 0x56: +M6502_ZEROX_READMODIFYWRITE +M6502_LSR +break; + +case 0x4e: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_LSR +break; + +case 0x5e: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_LSR +break; + + +case 0x1a: +case 0x3a: +case 0x5a: +case 0x7a: +case 0xda: +case 0xfa: +case 0xea: +M6502_IMPLIED +M6502_NOP +break; + +case 0x80: +case 0x82: +case 0xc2: +case 0xe2: +M6502_IMMEDIATE_READ +M6502_NOP +break; + +case 0x04: +case 0x44: +case 0x64: +M6502_ZERO_READ +M6502_NOP +break; + +case 0x14: +case 0x34: +case 0x54: +case 0x74: +case 0xd4: +case 0xf4: +M6502_ZEROX_READ +M6502_NOP +break; + +case 0x0c: +M6502_ABSOLUTE_READ +M6502_NOP +break; + +case 0x1c: +case 0x3c: +case 0x5c: +case 0x7c: +case 0xdc: +case 0xfc: +M6502_ABSOLUTEX_READ +M6502_NOP +break; + + +case 0x09: +M6502_IMMEDIATE_READ +M6502_ORA +break; + +case 0x05: +M6502_ZERO_READ +M6502_ORA +break; + +case 0x15: +M6502_ZEROX_READ +M6502_ORA +break; + +case 0x0D: +M6502_ABSOLUTE_READ +M6502_ORA +break; + +case 0x1D: +M6502_ABSOLUTEX_READ +M6502_ORA +break; + +case 0x19: +M6502_ABSOLUTEY_READ +M6502_ORA +break; + +case 0x01: +M6502_INDIRECTX_READ +M6502_ORA +break; + +case 0x11: +M6502_INDIRECTY_READ +M6502_ORA +break; + + +case 0x48: +M6502_IMPLIED +M6502_PHA +break; + + +case 0x08: +M6502_IMPLIED +M6502_PHP +break; + + +case 0x68: +M6502_IMPLIED +M6502_PLA +break; + + +case 0x28: +M6502_IMPLIED +M6502_PLP +break; + + +case 0x2f: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_RLA +break; + +case 0x3f: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_RLA +break; + +case 0x3b: +M6502_ABSOLUTEY_READMODIFYWRITE +M6502_RLA +break; + +case 0x27: +M6502_ZERO_READMODIFYWRITE +M6502_RLA +break; + +case 0x37: +M6502_ZEROX_READMODIFYWRITE +M6502_RLA +break; + +case 0x23: +M6502_INDIRECTX_READMODIFYWRITE +M6502_RLA +break; + +case 0x33: +M6502_INDIRECTY_READMODIFYWRITE +M6502_RLA +break; + + +case 0x2a: +M6502_IMPLIED +M6502_ROLA +break; + + +case 0x26: +M6502_ZERO_READMODIFYWRITE +M6502_ROL +break; + +case 0x36: +M6502_ZEROX_READMODIFYWRITE +M6502_ROL +break; + +case 0x2e: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_ROL +break; + +case 0x3e: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_ROL +break; + + +case 0x6a: +M6502_IMPLIED +M6502_RORA +break; + +case 0x66: +M6502_ZERO_READMODIFYWRITE +M6502_ROR +break; + +case 0x76: +M6502_ZEROX_READMODIFYWRITE +M6502_ROR +break; + +case 0x6e: +M6502_ABSOLUTE_READMODIFYWRITE +M6502_ROR +break; + +case 0x7e: +M6502_ABSOLUTEX_READMODIFYWRITE +M6502_ROR +break; + + +case 0x40: +M6502_IMPLIED +M6502_RTI +break; + + +case 0x60: +M6502_IMPLIED +M6502_RTS +break; + + +case 0xe9: +M6502_IMMEDIATE_READ +M6502_SBC +break; + +case 0xe5: +M6502_ZERO_READ +M6502_SBC +break; + +case 0xf5: +M6502_ZEROX_READ +M6502_SBC +break; + +case 0xed: +M6502_ABSOLUTE_READ +M6502_SBC +break; + +case 0xfd: +M6502_ABSOLUTEX_READ +M6502_SBC +break; + +case 0xf9: +M6502_ABSOLUTEY_READ +M6502_SBC +break; + +case 0xe1: +M6502_INDIRECTX_READ +M6502_SBC +break; + +case 0xf1: +M6502_INDIRECTY_READ +M6502_SBC +break; + + +case 0x38: +M6502_IMPLIED +M6502_SEC +break; + + +case 0xf8: +M6502_IMPLIED +M6502_SED +break; + + +case 0x78: +M6502_IMPLIED +M6502_SEI +break; + + +case 0x85: +M6502_ZERO_WRITE +M6502_STA +break; + +case 0x95: +M6502_ZEROX_WRITE +M6502_STA +break; + +case 0x8d: +M6502_ABSOLUTE_WRITE +M6502_STA +break; + +case 0x9d: +M6502_ABSOLUTEX_WRITE +M6502_STA +break; + +case 0x99: +M6502_ABSOLUTEY_WRITE +M6502_STA +break; + +case 0x81: +M6502_INDIRECTX_WRITE +M6502_STA +break; + +case 0x91: +M6502_INDIRECTY_WRITE +M6502_STA +break; + + +case 0x86: +M6502_ZERO_WRITE +M6502_STX +break; + +case 0x96: +M6502_ZEROY_WRITE +M6502_STX +break; + +case 0x8e: +M6502_ABSOLUTE_WRITE +M6502_STX +break; + + +case 0x84: +M6502_ZERO_WRITE +M6502_STY +break; + +case 0x94: +M6502_ZEROX_WRITE +M6502_STY +break; + +case 0x8c: +M6502_ABSOLUTE_WRITE +M6502_STY +break; + + +case 0xaa: +M6502_IMPLIED +M6502_TAX +break; + + +case 0xa8: +M6502_IMPLIED +M6502_TAY +break; + + +case 0xba: +M6502_IMPLIED +M6502_TSX +break; + + +case 0x8a: +M6502_IMPLIED +M6502_TXA +break; + + +case 0x9a: +M6502_IMPLIED +M6502_TXS +break; + + +case 0x98: +M6502_IMPLIED +M6502_TYA +break; + + diff --git a/stella/src/emucore/m6502/src/M6502Hi.cxx b/stella/src/emucore/m6502/src/M6502Hi.cxx new file mode 100644 index 000000000..e7605b27a --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502Hi.cxx @@ -0,0 +1,169 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Hi.cxx,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +//============================================================================ + +#include "M6502Hi.hxx" + +#define debugStream cout + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502High::M6502High(uInt32 systemCyclesPerProcessorCycle) + : M6502(systemCyclesPerProcessorCycle) +{ + myNumberOfDistinctAccesses = 0; + myLastAddress = 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502High::~M6502High() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline uInt8 M6502High::peek(uInt16 address) +{ + if(address != myLastAddress) + { + myNumberOfDistinctAccesses++; + myLastAddress = address; + } + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + return mySystem->peek(address); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline void M6502High::poke(uInt16 address, uInt8 value) +{ + if(address != myLastAddress) + { + myNumberOfDistinctAccesses++; + myLastAddress = address; + } + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + mySystem->poke(address, value); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool M6502High::execute(uInt32 number) +{ + // Clear all of the execution status bits except for the fatal error bit + myExecutionStatus &= FatalErrorBit; + + // Loop until execution is stopped or a fatal error occurs + for(;;) + { + for(; !myExecutionStatus && (number != 0); --number) + { + uInt16 operandAddress = 0; + uInt8 operand = 0; + +#ifdef DEBUG + debugStream << "PC=" << hex << setw(4) << PC << " "; +#endif + + // Fetch instruction at the program counter + IR = peek(PC++); + +#ifdef DEBUG + debugStream << "IR=" << hex << setw(2) << (int)IR << " "; + debugStream << "<" << ourAddressingModeTable[IR] << " "; +#endif + + // Call code to execute the instruction + switch(IR) + { + // 6502 instruction emulation is generated by an M4 macro file + #include "M6502Hi.ins" + + default: + // Oops, illegal instruction executed so set fatal error flag + myExecutionStatus |= FatalErrorBit; + } + +#ifdef DEBUG + debugStream << hex << setw(4) << operandAddress << " "; + debugStream << setw(4) << ourInstructionMnemonicTable[IR]; + debugStream << "> "; + debugStream << "A=" << ::hex << setw(2) << (int)A << " "; + debugStream << "X=" << ::hex << setw(2) << (int)X << " "; + debugStream << "Y=" << ::hex << setw(2) << (int)Y << " "; + debugStream << "PS=" << ::hex << setw(2) << (int)PS() << " "; + debugStream << "SP=" << ::hex << setw(2) << (int)SP << " "; + debugStream << "Cyc=" << dec << mySystem->cycles(); + debugStream << endl; +#endif + } + + // See if we need to handle an interrupt + if((myExecutionStatus & MaskableInterruptBit) || + (myExecutionStatus & NonmaskableInterruptBit)) + { + // Yes, so handle the interrupt + interruptHandler(); + } + + // See if execution has been stopped + if(myExecutionStatus & StopExecutionBit) + { + // Yes, so answer that everything finished fine + return true; + } + + // See if a fatal error has occured + if(myExecutionStatus & FatalErrorBit) + { + // Yes, so answer that something when wrong + return false; + } + + // See if we've executed the specified number of instructions + if(number == 0) + { + // Yes, so answer that everything finished fine + return true; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502High::interruptHandler() +{ + // Handle the interrupt + if((myExecutionStatus & MaskableInterruptBit) && !I) + { + mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle); + mySystem->poke(0x0100 + SP--, (PC - 1) >> 8); + mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff); + mySystem->poke(0x0100 + SP--, PS() & (~0x10)); + D = false; + I = true; + PC = (uInt16)mySystem->peek(0xFFFE) | ((uInt16)mySystem->peek(0xFFFF) << 8); + } + else if(myExecutionStatus & NonmaskableInterruptBit) + { + mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle); + mySystem->poke(0x0100 + SP--, (PC - 1) >> 8); + mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff); + mySystem->poke(0x0100 + SP--, PS() & (~0x10)); + D = false; + PC = (uInt16)mySystem->peek(0xFFFA) | ((uInt16)mySystem->peek(0xFFFB) << 8); + } + + // Clear the interrupt bits in myExecutionStatus + myExecutionStatus &= ~(MaskableInterruptBit | NonmaskableInterruptBit); +} + diff --git a/stella/src/emucore/m6502/src/M6502Hi.hxx b/stella/src/emucore/m6502/src/M6502Hi.hxx new file mode 100644 index 000000000..03d27aaf0 --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502Hi.hxx @@ -0,0 +1,108 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Hi.hxx,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +//============================================================================ + +#ifndef M6502HIGH_HXX +#define M6502HIGH_HXX + +class M6502High; + +#include "bspf.hxx" +#include "M6502.hxx" + +/** + This class provides a high compatibility 6502 microprocessor emulator. + The memory accesses and cycle counts it generates are valid at the + sub-instruction level and "false" reads are generated (such as the ones + produced by the Indirect,X addressing when it crosses a page boundary). + This provides provides better compatibility for hardware that has side + effects and for games which are very time sensitive. + + @author Bradford W. Mott + @version $Id: M6502Hi.hxx,v 1.1.1.1 2001-12-27 19:54:30 bwmott Exp $ +*/ +class M6502High : public M6502 +{ + public: + /** + Create a new high compatibility 6502 microprocessor with the + specified cycle multiplier. + + @param systemCyclesPerProcessorCycle The cycle multiplier + */ + M6502High(uInt32 systemCyclesPerProcessorCycle); + + /** + Destructor + */ + virtual ~M6502High(); + + public: + /** + Execute instructions until the specified number of instructions + is executed, someone stops execution, or an error occurs. Answers + true iff execution stops normally. + + @param number Indicates the number of instructions to execute + @return true iff execution stops normally + */ + virtual bool execute(uInt32 number); + + public: + /** + Get the number of memory accesses to distinct memory locations + + @return The number of memory accesses to distinct memory locations + */ + uInt32 distinctAccesses() const + { + return myNumberOfDistinctAccesses; + } + + protected: + /** + Called after an interrupt has be requested using irq() or nmi() + */ + void interruptHandler(); + + protected: + /* + Get the byte at the specified address and update the cycle + count + + @return The byte at the specified address + */ + inline uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value and + update the cycle count + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + inline void poke(uInt16 address, uInt8 value); + + private: + // Indicates the numer of distinct memory accesses + uInt32 myNumberOfDistinctAccesses; + + // Indicates the last address which was accessed + uInt16 myLastAddress; +}; +#endif + diff --git a/stella/src/emucore/m6502/src/M6502Hi.m4 b/stella/src/emucore/m6502/src/M6502Hi.m4 new file mode 100644 index 000000000..82a7b761a --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502Hi.m4 @@ -0,0 +1,314 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Hi.m4,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +/** + Code to handle addressing modes and branch instructions for + high compatibility emulation + + @author Bradford W. Mott + @version $Id: M6502Hi.m4,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + +define(M6502_IMPLIED, `{ + peek(PC); +}') + +define(M6502_IMMEDIATE_READ, `{ + operand = peek(PC++); +}') + +define(M6502_ABSOLUTE_READ, `{ + uInt16 address = peek(PC++); + address |= ((uInt16)peek(PC++) << 8); + operand = peek(address); +}') + +define(M6502_ABSOLUTE_WRITE, `{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); +}') + +define(M6502_ABSOLUTE_READMODIFYWRITE, `{ + operandAddress = peek(PC++); + operandAddress |= ((uInt16)peek(PC++) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_ABSOLUTEX_READ, `{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + X)); + if((low + X) > 0xFF) + operand = peek((high | low) + X); +}') + +define(M6502_ABSOLUTEX_WRITE, `{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; +}') + +define(M6502_ABSOLUTEX_READMODIFYWRITE, `{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + X)); + operandAddress = (high | low) + X; + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_ABSOLUTEY_READ, `{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +}') + +define(M6502_ABSOLUTEY_WRITE, `{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; +}') + +define(M6502_ABSOLUTEY_READMODIFYWRITE, `{ + uInt16 low = peek(PC++); + uInt16 high = ((uInt16)peek(PC++) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_ZERO_READ, `{ + operand = peek(peek(PC++)); +}') + +define(M6502_ZERO_WRITE, `{ + operandAddress = peek(PC++); +}') + +define(M6502_ZERO_READMODIFYWRITE, `{ + operandAddress = peek(PC++); + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_ZEROX_READ, `{ + uInt8 address = peek(PC++); + peek(address); + address += X; + operand = peek(address); +}') + +define(M6502_ZEROX_WRITE, `{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; +}') + +define(M6502_ZEROX_READMODIFYWRITE, `{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + X) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_ZEROY_READ, `{ + uInt8 address = peek(PC++); + peek(address); + address += Y; + operand = peek(address); +}') + +define(M6502_ZEROY_WRITE, `{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + Y) & 0xFF; +}') + +define(M6502_ZEROY_READMODIFYWRITE, `{ + operandAddress = peek(PC++); + peek(operandAddress); + operandAddress = (operandAddress + Y) & 0xFF; + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_INDIRECT, `{ + uInt16 addr = peek(PC++); + addr |= ((uInt16)peek(PC++) << 8); + + // Simulate the error in the indirect addressing mode! + uInt16 high = NOTSAMEPAGE(addr, addr + 1) ? (addr & 0xff00) : (addr + 1); + + operandAddress = peek(addr); + operandAddress |= ((uInt16)peek(high) << 8); +}') + +define(M6502_INDIRECTX_READ, `{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + uInt16 address = peek(pointer++); + address |= ((uInt16)peek(pointer) << 8); + operand = peek(address); +}') + +define(M6502_INDIRECTX_WRITE, `{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + operandAddress = peek(pointer++); + operandAddress |= ((uInt16)peek(pointer) << 8); +}') + +define(M6502_INDIRECTX_READMODIFYWRITE, `{ + uInt8 pointer = peek(PC++); + peek(pointer); + pointer += X; + operandAddress = peek(pointer++); + operandAddress |= ((uInt16)peek(pointer) << 8); + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + +define(M6502_INDIRECTY_READ, `{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + operand = peek(high | (uInt8)(low + Y)); + if((low + Y) > 0xFF) + operand = peek((high | low) + Y); +}') + +define(M6502_INDIRECTY_WRITE, `{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; +}') + +define(M6502_INDIRECTY_READMODIFYWRITE, `{ + uInt8 pointer = peek(PC++); + uInt16 low = peek(pointer++); + uInt16 high = ((uInt16)peek(pointer) << 8); + peek(high | (uInt8)(low + Y)); + operandAddress = (high | low) + Y; + operand = peek(operandAddress); + poke(operandAddress, operand); +}') + + +define(M6502_BCC, `{ + if(!C) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BCS, `{ + if(C) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BEQ, `{ + if(!notZ) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BMI, `{ + if(N) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BNE, `{ + if(notZ) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BPL, `{ + if(!N) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BVC, `{ + if(!V) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + +define(M6502_BVS, `{ + if(V) + { + peek(PC); + uInt16 address = PC + (Int8)operand; + if(NOTSAMEPAGE(PC, address)) + peek((PC & 0xFF00) | (address & 0x00FF)); + PC = address; + } +}') + diff --git a/stella/src/emucore/m6502/src/M6502Low.cxx b/stella/src/emucore/m6502/src/M6502Low.cxx new file mode 100644 index 000000000..2d36784b1 --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502Low.cxx @@ -0,0 +1,159 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Low.cxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +#include "M6502Low.hxx" + +#define debugStream cout + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502Low::M6502Low(uInt32 systemCyclesPerProcessorCycle) + : M6502(systemCyclesPerProcessorCycle) +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +M6502Low::~M6502Low() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline uInt8 M6502Low::peek(uInt16 address) +{ + return mySystem->peek(address); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline void M6502Low::poke(uInt16 address, uInt8 value) +{ + mySystem->poke(address, value); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool M6502Low::execute(uInt32 number) +{ + // Clear all of the execution status bits except for the fatal error bit + myExecutionStatus &= FatalErrorBit; + + // Loop until execution is stopped or a fatal error occurs + for(;;) + { + for(; !myExecutionStatus && (number != 0); --number) + { + uInt16 operandAddress = 0; + uInt8 operand = 0; + +#ifdef DEBUG + debugStream << "PC=" << hex << setw(4) << PC << " "; +#endif + + // Fetch instruction at the program counter + IR = peek(PC++); + +#ifdef DEBUG + debugStream << "IR=" << hex << setw(2) << (int)IR << " "; + debugStream << "<" << ourAddressingModeTable[IR] << " "; +#endif + + // Update system cycles + mySystem->incrementCycles(myInstructionSystemCycleTable[IR]); + + // Call code to execute the instruction + switch(IR) + { + // 6502 instruction emulation is generated by an M4 macro file + #include "M6502Low.ins" + + default: + // Oops, illegal instruction executed so set fatal error flag + myExecutionStatus |= FatalErrorBit; + cerr << "Illegal Instruction! " << hex << (int) IR << endl; + } + +#ifdef DEBUG + debugStream << hex << setw(4) << operandAddress << " "; + debugStream << setw(4) << ourInstructionMnemonicTable[IR]; + debugStream << "> "; + debugStream << "A=" << ::hex << setw(2) << (int)A << " "; + debugStream << "X=" << ::hex << setw(2) << (int)X << " "; + debugStream << "Y=" << ::hex << setw(2) << (int)Y << " "; + debugStream << "PS=" << ::hex << setw(2) << (int)PS() << " "; + debugStream << "SP=" << ::hex << setw(2) << (int)SP << " "; + debugStream << "Cyc=" << dec << mySystem->cycles(); + debugStream << endl; +#endif + } + + // See if we need to handle an interrupt + if((myExecutionStatus & MaskableInterruptBit) || + (myExecutionStatus & NonmaskableInterruptBit)) + { + // Yes, so handle the interrupt + interruptHandler(); + } + + // See if execution has been stopped + if(myExecutionStatus & StopExecutionBit) + { + // Yes, so answer that everything finished fine + return true; + } + + // See if a fatal error has occured + if(myExecutionStatus & FatalErrorBit) + { + // Yes, so answer that something when wrong + return false; + } + + // See if we've executed the specified number of instructions + if(number == 0) + { + // Yes, so answer that everything finished fine + return true; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void M6502Low::interruptHandler() +{ + // Handle the interrupt + if((myExecutionStatus & MaskableInterruptBit) && !I) + { + mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle); + mySystem->poke(0x0100 + SP--, (PC - 1) >> 8); + mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff); + mySystem->poke(0x0100 + SP--, PS() & (~0x10)); + D = false; + I = true; + PC = (uInt16)mySystem->peek(0xFFFE) | ((uInt16)mySystem->peek(0xFFFF) << 8); + } + else if(myExecutionStatus & NonmaskableInterruptBit) + { + mySystem->incrementCycles(7 * mySystemCyclesPerProcessorCycle); + mySystem->poke(0x0100 + SP--, (PC - 1) >> 8); + mySystem->poke(0x0100 + SP--, (PC - 1) & 0x00ff); + mySystem->poke(0x0100 + SP--, PS() & (~0x10)); + D = false; + PC = (uInt16)mySystem->peek(0xFFFA) | ((uInt16)mySystem->peek(0xFFFB) << 8); + } + + // Clear the interrupt bits in myExecutionStatus + myExecutionStatus &= ~(MaskableInterruptBit | NonmaskableInterruptBit); +} + diff --git a/stella/src/emucore/m6502/src/M6502Low.hxx b/stella/src/emucore/m6502/src/M6502Low.hxx new file mode 100644 index 000000000..097d93162 --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502Low.hxx @@ -0,0 +1,94 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Low.hxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +#ifndef M6507LOW_HXX +#define M6507LOW_HXX + +class M6507Low; + +#include "bspf.hxx" +#include "M6502.hxx" + +/** + This class provides a low compatibility 6502 microprocessor emulator. + The memory accesses and cycle updates of this emulator are not 100% + accurate as shown below: + + 1. Only memory accesses which are actually needed are done + (i.e. no "false" reads and writes are performed) + + 2. Cycle counts are updated at the beginning of the instruction + execution and not valid at the sub-instruction level + + If speed is the most important issue then use this class, however, if + better compatibility is neccessary use one of the other 6502 classes. + + @author Bradford W. Mott + @version $Id: M6502Low.hxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +*/ +class M6502Low : public M6502 +{ + public: + /** + Create a new low compatibility 6502 microprocessor with the specified + cycle multiplier. + + @param systemCyclesPerProcessorCycle The cycle multiplier + */ + M6502Low(uInt32 systemCyclesPerProcessorCycle); + + /** + Destructor + */ + virtual ~M6502Low(); + + public: + /** + Execute instructions until the specified number of instructions + is executed, someone stops execution, or an error occurs. Answers + true iff execution stops normally. + + @param number Indicates the number of instructions to execute + @return true iff execution stops normally + */ + virtual bool execute(uInt32 number); + + protected: + /** + Called after an interrupt has be requested using irq() or nmi() + */ + void interruptHandler(); + + protected: + /* + Get the byte at the specified address + + @return The byte at the specified address + */ + inline uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + inline void poke(uInt16 address, uInt8 value); +}; +#endif + diff --git a/stella/src/emucore/m6502/src/M6502Low.m4 b/stella/src/emucore/m6502/src/M6502Low.m4 new file mode 100644 index 000000000..e0621be8a --- /dev/null +++ b/stella/src/emucore/m6502/src/M6502Low.m4 @@ -0,0 +1,286 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: M6502Low.m4,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +/** + Code to handle addressing modes and branch instructions for + low compatibility emulation + + @author Bradford W. Mott + @version $Id: M6502Low.m4,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +*/ + +#ifndef NOTSAMEPAGE + #define NOTSAMEPAGE(_addr1, _addr2) (((_addr1) ^ (_addr2)) & 0xff00) +#endif + +define(M6502_IMPLIED, `{ +}') + +define(M6502_IMMEDIATE_READ, `{ + operandAddress = PC++; + operand = peek(operandAddress); +}') + +define(M6502_ABSOLUTE_READ, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +}') + +define(M6502_ABSOLUTE_WRITE, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; +}') + +define(M6502_ABSOLUTE_READMODIFYWRITE, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operand = peek(operandAddress); +}') + +define(M6502_ABSOLUTEX_READ, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + X)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += X; + operand = peek(operandAddress); +}') + +define(M6502_ABSOLUTEX_WRITE, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; +}') + +define(M6502_ABSOLUTEX_READMODIFYWRITE, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += X; + operand = peek(operandAddress); +}') + +define(M6502_ABSOLUTEY_READ, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // See if we need to add one cycle for indexing across a page boundary + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +}') + +define(M6502_ABSOLUTEY_WRITE, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += Y; +}') + +define(M6502_ABSOLUTEY_READMODIFYWRITE, `{ + operandAddress = (uInt16)peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + operandAddress += Y; + operand = peek(operandAddress); +}') + +define(M6502_ZERO_READ, `{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +}') + +define(M6502_ZERO_WRITE, `{ + operandAddress = peek(PC++); +}') + +define(M6502_ZERO_READMODIFYWRITE, `{ + operandAddress = peek(PC++); + operand = peek(operandAddress); +}') + +define(M6502_ZEROX_READ, `{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +}') + +define(M6502_ZEROX_WRITE, `{ + operandAddress = (uInt8)(peek(PC++) + X); +}') + +define(M6502_ZEROX_READMODIFYWRITE, `{ + operandAddress = (uInt8)(peek(PC++) + X); + operand = peek(operandAddress); +}') + +define(M6502_ZEROY_READ, `{ + operandAddress = (uInt8)(peek(PC++) + Y); + operand = peek(operandAddress); +}') + +define(M6502_ZEROY_WRITE, `{ + operandAddress = (uInt8)(peek(PC++) + Y); +}') + +define(M6502_ZEROY_READMODIFYWRITE, `{ + operandAddress = (uInt8)(peek(PC++) + Y); + operand = peek(operandAddress); +}') + +define(M6502_INDIRECT, `{ + uInt16 addr = peek(PC) | ((uInt16)peek(PC + 1) << 8); + PC += 2; + + // Simulate the error in the indirect addressing mode! + uInt16 high = NOTSAMEPAGE(addr, addr + 1) ? (addr & 0xff00) : (addr + 1); + + operandAddress = peek(addr) | ((uInt16)peek(high) << 8); +}') + +define(M6502_INDIRECTX_READ, `{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +}') + +define(M6502_INDIRECTX_WRITE, `{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); +}') + +define(M6502_INDIRECTX_READMODIFYWRITE, `{ + uInt8 pointer = peek(PC++) + X; + operandAddress = peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operand = peek(operandAddress); +}') + +define(M6502_INDIRECTY_READ, `{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + + if(NOTSAMEPAGE(operandAddress, operandAddress + Y)) + { + mySystem->incrementCycles(mySystemCyclesPerProcessorCycle); + } + + operandAddress += Y; + operand = peek(operandAddress); +}') + +define(M6502_INDIRECTY_WRITE, `{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operandAddress += Y; +}') + +define(M6502_INDIRECTY_READMODIFYWRITE, `{ + uInt8 pointer = peek(PC++); + operandAddress = (uInt16)peek(pointer) | ((uInt16)peek(pointer + 1) << 8); + operandAddress += Y; + operand = peek(operandAddress); +}') + + +define(M6502_BCC, `{ + if(!C) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BCS, `{ + if(C) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BEQ, `{ + if(!notZ) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BMI, `{ + if(N) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BNE, `{ + if(notZ) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BPL, `{ + if(!N) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BVC, `{ + if(!V) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + +define(M6502_BVS, `{ + if(V) + { + uInt16 address = PC + (Int8)operand; + mySystem->incrementCycles(NOTSAMEPAGE(PC, address) ? + mySystemCyclesPerProcessorCycle << 1 : mySystemCyclesPerProcessorCycle); + PC = address; + } +}') + + diff --git a/stella/src/emucore/m6502/src/NullDev.cxx b/stella/src/emucore/m6502/src/NullDev.cxx new file mode 100644 index 000000000..b0a0f63a9 --- /dev/null +++ b/stella/src/emucore/m6502/src/NullDev.cxx @@ -0,0 +1,60 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: NullDev.cxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +#include "NullDev.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +NullDevice::NullDevice() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +NullDevice::~NullDevice() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const char* NullDevice::name() const +{ + return "NULL"; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void NullDevice::reset() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void NullDevice::install(System& system) +{ + mySystem = &system; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 NullDevice::peek(uInt16 address) +{ + cerr << hex << "NullDevice: peek(" << address << ")" << endl; + return 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void NullDevice::poke(uInt16 address, uInt8 value) +{ + cerr << hex << "NullDevice: poke(" << address << "," << value << ")" << endl; +} + diff --git a/stella/src/emucore/m6502/src/NullDev.hxx b/stella/src/emucore/m6502/src/NullDev.hxx new file mode 100644 index 000000000..d75e248c3 --- /dev/null +++ b/stella/src/emucore/m6502/src/NullDev.hxx @@ -0,0 +1,86 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: NullDev.hxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +#ifndef NULLDEVICE_HXX +#define NULLDEVICE_HXX + +class System; + +#include "bspf.hxx" +#include "Device.hxx" + +/** + Class that represents a "null" device. The basic idea is that a + null device is installed in a 6502 based system anywhere there are + holes in the address space (i.e. no real device attached). + + @author Bradford W. Mott + @version $Id: NullDev.hxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +*/ +class NullDevice : public Device +{ + public: + /** + Create a new null device + */ + NullDevice(); + + /** + Destructor + */ + virtual ~NullDevice(); + + public: + /** + Get a null terminated string which is the device's name (i.e. "M6532") + + @return The name of the device + */ + virtual const char* name() const; + + /** + Reset device to its power-on state + */ + virtual void reset(); + + /** + Install device in the specified system. Invoked by the system + when the device is attached to it. + + @param system The system the device should install itself in + */ + virtual void install(System& system); + + public: + /** + Get the byte at the specified address + + @return The byte at the specified address + */ + virtual uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + virtual void poke(uInt16 address, uInt8 value); +}; +#endif + diff --git a/stella/src/emucore/m6502/src/System.cxx b/stella/src/emucore/m6502/src/System.cxx new file mode 100644 index 000000000..e2f26d155 --- /dev/null +++ b/stella/src/emucore/m6502/src/System.cxx @@ -0,0 +1,160 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: System.cxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +#include +#include "Device.hxx" +#include "M6502.hxx" +#include "System.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +System::System(uInt16 n, uInt16 m) + : myAddressMask((1 << n) - 1), + myPageShift(m), + myPageMask((1 << m) - 1), + myNumberOfPages(1 << (n - m)), + myNumberOfDevices(0), + myM6502(0), + myCycles(0) +{ + // Make sure the arguments are reasonable + assert((1 <= m) && (m <= n) && (n <= 16)); + + // Allocate page table + myPageAccessTable = new PageAccess[myNumberOfPages]; + + // Initialize page access table + PageAccess access; + access.directPeekBase = 0; + access.directPokeBase = 0; + access.device = &myNullDevice; + for(int page = 0; page < myNumberOfPages; ++page) + { + setPageAccess(page, access); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +System::~System() +{ + // Free the devices attached to me, since I own them + for(uInt32 i = 0; i < myNumberOfDevices; ++i) + { + delete myDevices[i]; + } + + // Free the M6502 that I own + delete myM6502; + + // Free my page access table + delete[] myPageAccessTable; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void System::reset() +{ + // Reset system cycle counter + resetCycles(); + + // Frist we reset the devices attached to myself + for(uInt32 i = 0; i < myNumberOfDevices; ++i) + { + myDevices[i]->reset(); + } + + // Now we reset the processor if it exists + if(myM6502 != 0) + { + myM6502->reset(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void System::attach(Device* device) +{ + assert(myNumberOfDevices < 100); + + // Add device to my collection of devices + myDevices[myNumberOfDevices++] = device; + + // Ask the device to install itself + device->install(*this); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void System::attach(M6502* m6502) +{ + // Remember the processor + myM6502 = m6502; + + // Ask the processor to install itself + myM6502->install(*this); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void System::resetCycles() +{ + // Frist we let all of the device attached to me know about the reset + for(uInt32 i = 0; i < myNumberOfDevices; ++i) + { + myDevices[i]->systemCyclesReset(); + } + + // Now, we reset cycle count to zero + myCycles = 0; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void System::setPageAccess(uInt16 page, const PageAccess& access) +{ + // Make sure the page is within range + assert(page <= myNumberOfPages); + + // Make sure the access methods make sense + assert(access.device != 0); + + myPageAccessTable[page] = access; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const System::PageAccess& System::getPageAccess(uInt16 page) +{ + // Make sure the page is within range + assert(page <= myNumberOfPages); + + return myPageAccessTable[page]; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +System::System(const System& s) + : myAddressMask(s.myAddressMask), + myPageShift(s.myPageShift), + myPageMask(s.myPageMask), + myNumberOfPages(s.myNumberOfPages) +{ + assert(false); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +System& System::operator = (const System&) +{ + assert(false); + + return *this; +} + + diff --git a/stella/src/emucore/m6502/src/System.hxx b/stella/src/emucore/m6502/src/System.hxx new file mode 100644 index 000000000..125a92aaf --- /dev/null +++ b/stella/src/emucore/m6502/src/System.hxx @@ -0,0 +1,310 @@ +//============================================================================ +// +// MM MM 6666 555555 0000 2222 +// MMMM MMMM 66 66 55 00 00 22 22 +// MM MMM MM 66 55 00 00 22 +// MM M MM 66666 55555 00 00 22222 -- "A 6502 Microprocessor Emulator" +// MM MM 66 66 55 00 00 22 +// MM MM 66 66 55 55 00 00 22 +// MM MM 6666 5555 0000 222222 +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: System.hxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +//============================================================================ + +#ifndef SYSTEM_HXX +#define SYSTEM_HXX + +class Device; +class M6502; +class NullDevice; + +#include "bspf.hxx" +#include "Device.hxx" +#include "NullDev.hxx" + +/** + This class represents a system consisting of a 6502 microprocessor + and a set of devices. The devices are mapped into an addressing + space of 2^n bytes (1 <= n <= 16). The addressing space is broken + into 2^m byte pages (1 <= m <= n), where a page is the smallest unit + a device can use when installing itself in the system. + + In general the addressing space will be 8192 (2^13) bytes for a + 6507 based system and 65536 (2^16) bytes for a 6502 based system. + + TODO: To allow for dynamic code generation we probably need to + add a tag to each page that indicates if it is read only + memory. We also need to notify the processor anytime a + page access method is changed so that it can clear the + dynamic code for that page of memory. + + @author Bradford W. Mott + @version $Id: System.hxx,v 1.1.1.1 2001-12-27 19:54:31 bwmott Exp $ +*/ +class System +{ + public: + /** + Create a new system with an addressing space of 2^n bytes and + pages of 2^m bytes. + + @param n Log base 2 of the addressing space size + @param m Log base 2 of the page size + */ + System(uInt16 n, uInt16 m); + + /** + Destructor + */ + virtual ~System(); + + public: + /** + Reset the system cycle counter, the attached devices, and the + attached processor of the system. + */ + void reset(); + + public: + /** + Attach the specified device and claim ownership of it. The device + will be asked to install itself. + + @param device The device to attach to the system + */ + void attach(Device* device); + + /** + Attach the specified processor and claim ownership of it. The + processor will be asked to install itself. + + @param m6502 The 6502 microprocessor to attach to the system + */ + void attach(M6502* m6502); + + public: + /** + Answer the 6502 microprocessor attached to the system. If a + processor has not been attached calling this function will fail. + + @return The attached 6502 microprocessor + */ + M6502& m6502() + { + return *myM6502; + } + + /** + Get the null device associated with the system. Every system + has a null device associated with it that's used by pages which + aren't mapped to "real" devices. + + @return The null device associated with the system + */ + NullDevice& nullDevice() + { + return myNullDevice; + } + + /** + Get the total number of pages available in the system. + + @return The total number of pages available + */ + uInt16 numberOfPages() const + { + return myNumberOfPages; + } + + /** + Get the amount to right shift an address by to obtain its page. + + @return The amount to right shift an address by to get its page + */ + uInt16 pageShift() const + { + return myPageShift; + } + + /** + Get the mask to apply to an address to obtain its page offset. + + @return The mask to apply to an address to obtain its page offset + */ + uInt16 pageMask() const + { + return myPageMask; + } + + public: + /** + Get the number of system cycles which have passed since the last + time cycles were reset or the system was reset. + + @return The number of system cycles which have passed + */ + uInt32 cycles() const + { + return myCycles; + } + + /** + Increment the system cycles by the specified number of cycles. + + @param amount The amount to add to the system cycles counter + */ + void incrementCycles(uInt32 amount) + { + myCycles += amount; + } + + /** + Reset the system cycle count to zero. The first thing that + happens is that all devices are notified of the reset by invoking + their systemCyclesReset method then the system cycle count is + reset to zero. + */ + void resetCycles(); + + public: + /* + Get the byte at the specified address. No masking of the + address occurs before it's sent to the device mapped at + the address. + + @return The byte at the specified address + */ + uInt8 peek(uInt16 address); + + /** + Change the byte at the specified address to the given value. + No masking of the address occurs before it's sent to the device + mapped at the address. + + @param address The address where the value should be stored + @param value The value to be stored at the address + */ + void poke(uInt16 address, uInt8 value); + + public: + /** + Structure used to specify access methods for a page + */ + struct PageAccess + { + /** + Pointer to a block of memory or the null pointer. The null pointer + indicates that the device's peek method should be invoked for reads + to this page, while other values are the base address of an array + to directly access for reads to this page. + */ + uInt8* directPeekBase; + + /** + Pointer to a block of memory or the null pointer. The null pointer + indicates that the device's poke method should be invoked for writes + to this page, while other values are the base address of an array + to directly access for pokes to this page. + */ + uInt8* directPokeBase; + + /** + Pointer to the device associated with this page or to the system's + null device if the page hasn't been mapped to a device + */ + Device* device; + }; + + /** + Set the page accessing method for the specified page. + + @param page The page accessing methods should be set for + @param access The accessing methods to be used by the page + */ + void setPageAccess(uInt16 page, const PageAccess& access); + + /** + Get the page accessing method for the specified page. + + @param page The page to get accessing methods for + @return The accessing methods used by the page + */ + const PageAccess& getPageAccess(uInt16 page); + + private: + // Mask to apply to an address before accessing memory + const uInt16 myAddressMask; + + // Amount to shift an address by to determine what page it's on + const uInt16 myPageShift; + + // Mask to apply to an address to obtain its page offset + const uInt16 myPageMask; + + // Number of pages in the system + const uInt16 myNumberOfPages; + + // Pointer to a dynamically allocated array of PageAccess structures + PageAccess* myPageAccessTable; + + // Array of all the devices attached to the system + Device* myDevices[100]; + + // Number of devices attached to the system + uInt32 myNumberOfDevices; + + // 6502 processor attached to the system or the null pointer + M6502* myM6502; + + // Number of system cycles executed since the last reset + uInt32 myCycles; + + // Null device to use for page which are not installed + NullDevice myNullDevice; + + private: + // Copy constructor isn't supported by this class so make it private + System(const System&); + + // Assignment operator isn't supported by this class so make it private + System& operator = (const System&); +}; + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline uInt8 System::peek(uInt16 addr) +{ + PageAccess& access = myPageAccessTable[(addr & myAddressMask) >> myPageShift]; + + // See if this page uses direct accessing or not + if(access.directPeekBase != 0) + { + return *(access.directPeekBase + (addr & myPageMask)); + } + else + { + return access.device->peek(addr); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +inline void System::poke(uInt16 addr, uInt8 value) +{ + PageAccess& access = myPageAccessTable[(addr & myAddressMask) >> myPageShift]; + + // See if this page uses direct accessing or not + if(access.directPokeBase != 0) + { + *(access.directPokeBase + (addr & myPageMask)) = value; + } + else + { + access.device->poke(addr, value); + } +} +#endif + diff --git a/stella/src/emucore/m6502/src/bspf/Copyright.txt b/stella/src/emucore/m6502/src/bspf/Copyright.txt new file mode 100644 index 000000000..5888122b0 --- /dev/null +++ b/stella/src/emucore/m6502/src/bspf/Copyright.txt @@ -0,0 +1,37 @@ +=============================================================================== + + BBBBB SSSS PPPPP FFFFFF + BB BB SS SS PP PP FF + BB BB SS PP PP FF + BBBBB SSSS PPPPP FFFF -- "Brad's Simple Portability Framework" + BB BB SS PP FF + BB BB SS SS PP FF + BBBBB SSSS PP FF + +=============================================================================== + License Information and Copyright Notice +=============================================================================== + +Copyright (C) 1995-2002 Bradford W. Mott + +This program is free software; you can redistribute it and/or modify it under +the terms of the GNU General Public License as published by the Free Software +Foundation; either version 2 of the License, or any later version. + +You should have received a copy of the GNU General Public License version 2 +along with this program (License.txt); if not, write to the Free Software +Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA. + +This program is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY. IN NO EVENT SHALL THE AUTHORS OR DISTRIBUTORS BE LIABLE TO ANY +PARTY FOR DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES +ARISING OUT OF THE USE OF THIS SOFTWARE, ITS DOCUMENTATION, OR ANY DERIVATIVES +THEREOF, EVEN IF THE AUTHORS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGE. + +THE AUTHORS AND DISTRIBUTORS SPECIFICALLY DISCLAIM ANY WARRANTIES, INCLUDING, +BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +PARTICULAR PURPOSE, AND NON-INFRINGEMENT. THIS SOFTWARE IS PROVIDED ON AN +"AS IS" BASIS, AND THE AUTHORS AND DISTRIBUTORS HAVE NO OBLIGATION TO PROVIDE +MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + diff --git a/stella/src/emucore/m6502/src/bspf/License.txt b/stella/src/emucore/m6502/src/bspf/License.txt new file mode 100644 index 000000000..4189933be --- /dev/null +++ b/stella/src/emucore/m6502/src/bspf/License.txt @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + Appendix: How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + + Copyright (C) 19yy + + This program is free software; you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation; either version 2 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111 USA + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) 19yy name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + , 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/stella/src/emucore/m6502/src/bspf/src/bspf.hxx b/stella/src/emucore/m6502/src/bspf/src/bspf.hxx new file mode 100644 index 000000000..4d361df99 --- /dev/null +++ b/stella/src/emucore/m6502/src/bspf/src/bspf.hxx @@ -0,0 +1,72 @@ +//============================================================================ +// +// BBBBB SSSS PPPPP FFFFFF +// BB BB SS SS PP PP FF +// BB BB SS PP PP FF +// BBBBB SSSS PPPPP FFFF -- "Brad's Simple Portability Framework" +// BB BB SS PP FF +// BB BB SS SS PP FF +// BBBBB SSSS PP FF +// +// Copyright (c) 1997-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: bspf.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +//============================================================================ + +#ifndef BSPF_HXX +#define BSPF_HXX + +/** + This file defines various basic data types and preprocessor variables + that need to be defined for different operating systems. + + @author Bradford W. Mott + @version $Id: bspf.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +*/ + +// Types for 8-bit signed and unsigned integers +typedef signed char Int8; +typedef unsigned char uInt8; + +// Types for 16-bit signed and unsigned integers +typedef signed short Int16; +typedef unsigned short uInt16; + +// Types for 32-bit signed and unsigned integers +typedef signed int Int32; +typedef unsigned int uInt32; + +// The following code should provide access to the standard C++ objects and +// types: cout, cerr, string, ostream, istream, etc. +#ifdef BSPF_WIN32 + #include + #include + #include + using namespace std; +#else + #include + #include + #include +#endif + +// Some compilers do not support the bool type yet :-( +#ifdef BSPF_BOOL + #define bool int + #define true 1 + #define false 0 +#endif + +// Defines to help with path handling +#if defined BSPF_UNIX + #define BSPF_PATH_SEPARATOR '/' +#elif (defined(BSPF_DOS) || defined(BSPF_WIN32) || defined(BSPF_OS2)) + #define BSPF_PATH_SEPARATOR '\\' +#elif defined BSPF_MACOS + #define BSPF_PATH_SEPARATOR ':' +#endif + +#endif + diff --git a/stella/src/emucore/misc/romtohex.cxx b/stella/src/emucore/misc/romtohex.cxx new file mode 100644 index 000000000..eacc15df9 --- /dev/null +++ b/stella/src/emucore/misc/romtohex.cxx @@ -0,0 +1,29 @@ +/** + Simple program that produces a hex list of a binary object file + + @author Bradford W. Mott + @version $Id: romtohex.cxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +*/ + +#include +#include + +main() +{ + ifstream in("rom.o"); + + for(int t = 0; ; ++t) + { + unsigned char c; + in.get(c); + + if(in.eof()) + break; + + cout << "0x" << hex << (int)c << ", "; + + if((t % 8) == 7) + cout << endl; + } + cout << endl; +} diff --git a/stella/src/emucore/misc/scrom.asm b/stella/src/emucore/misc/scrom.asm new file mode 100644 index 000000000..1313889c7 --- /dev/null +++ b/stella/src/emucore/misc/scrom.asm @@ -0,0 +1,81 @@ +;;============================================================================ +;; +;; SSSS tt lll lll +;; SS SS tt ll ll +;; SS tttttt eeee ll ll aaaa +;; SSSS tt ee ee ll ll aa +;; SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +;; SS SS tt ee ll ll aa aa +;; SSSS ttt eeeee llll llll aaaaa +;; +;; Copyright (c) 1995-1998 by Bradford W. Mott +;; +;; See the file "license" for information on usage and redistribution of +;; this file, and for a DISCLAIMER OF ALL WARRANTIES. +;; +;; $Id: scrom.asm,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +;;============================================================================ +;; +;; This file contains a "dummy" Supercharger ROM for Stella. It basically +;; contains some bootstrapping code to get the game up and running. +;; +;;============================================================================ + + processor 6502 + + org $FA00 + +;; +;; Normal clear page zero routine for initial program load +;; + LDA #0 + LDX #0 +clear STA $80,X + INX + CPX #$80 + BNE clear + JMP cpcode + +;; +;; Clear page zero routine for multi-load +;; + org $FA20 + + LDA #0 + LDX #0 +mlclr STA $80,X + INX + CPX #$1E + BNE mlclr + +;; +;; Now, copy some code into page zero to do the initial bank switch +;; +cpcode LDX #0 +copy LDA code,X + STA $fa,X + INX + CPX #6 + BNE copy + +;; +;; Initialize X and Y registers +;; + LDX #$ff + LDY #$00 + +;; +;; Store the bank configuration in $80 and in CMP instruction +;; + LDA #$00 ;; $00 is changed by emulator to the correct value + STA $80 + + CMP $f000 ;; $00 is changed by emulator to the correct value +;; +;; Execute the code to do bank switch and start running cartridge code +;; + JMP $fa + +code dc.b $ad, $f8, $ff ;; LDA $fff8 + dc.b $4c, $00, $00 ;; JMP $???? ($???? is filled in by emulator) + diff --git a/stella/src/emucore/stella.pro b/stella/src/emucore/stella.pro new file mode 100644 index 000000000..ad8191087 --- /dev/null +++ b/stella/src/emucore/stella.pro @@ -0,0 +1,5246 @@ +; +; LAST MODIFIED: 22 March 1999 1:32PM +; + +"Cartridge.MD5" "0db4f4150fecf77e4ce72ca4d04c052f" +"Cartridge.Name" "3D Tic-Tac-Toe" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2618 / 4975123" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "37" +"" + +"Cartridge.MD5" "291bcdb05f2b37cdf9452d2bf08e0321" +"Cartridge.Name" "32 in 1" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26163" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "c00734a2233ef683d9b6e622ac97a5c8" +"Cartridge.Name" "A-Team, The" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26133" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.YStart" "44" +"Display.Height" "190" +"" + +"Cartridge.MD5" "17ee23e5da931be82f733917adcb6386" +"Cartridge.Name" "Acid Drop" +"Cartridge.Manufacturer" "Salu" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "57" +"Cartridge.Type" "F6" +"Display.YStart" "52" +"Display.Height" "255" +"" + +"Cartridge.MD5" "d573089534ca596e64efef474be7b6bc" +"Cartridge.Name" "Action Man" +"Cartridge.Note" "Uses paddle/joystick combination; variant to G.I. Joe-Cobra Strike by Parker Bros." +"Display.YStart" "57" +"Controller.Left" "Paddles" +"" + +"Cartridge.MD5" "157bddb7192754a45372be196797f284" +"Cartridge.Name" "Adventure" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2613 / 4975154" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Cartridge.Note" "One player joystick only" +"Display.YStart" "37" +"Display.Height" "192" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "ca4f8c5b4d6fb9d608bb96bc7ebd26c7" +"Cartridge.Name" "Adventures of Tron" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT4317" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.YStart" "46" +"Display.Height" "180" +"" + +"Cartridge.MD5" "c4bc8c2e130d76346ebf8eb544991b46" +"Cartridge.Name" "Advertisement Cartridge" +"" + +"Cartridge.MD5" "98e5e4d5c4dd9a986d30fd62bd2f75ae" +"Cartridge.Name" "Air-Sea Battle (1)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2602" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "2K" +"Display.FrameRate" "30" +"Display.YStart" "39" +"Display.Height" "200" +"" + +"Cartridge.MD5" "1d1d2603ec139867c1d1f5ddf83093f1" +"Cartridge.Name" "Air-Sea Battle (2)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2602" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "2K" +"Display.FrameRate" "30" +"Display.YStart" "39" +"Display.Height" "200" +"" + +"Cartridge.MD5" "16cb43492987d2f32b423817cdaaf7c4" +"Cartridge.Name" "Air-Sea Battle (3)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2602" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "2K" +"Display.FrameRate" "30" +"Display.YStart" "39" +"Display.Height" "200" +"" + +"Cartridge.MD5" "4d77f291dca1518d7d8e47838695f54b" +"Cartridge.Name" "Airlock" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "DA 1004" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "a9cb638cd2cb2e8e0643d7a67db4281c" +"Cartridge.Name" "Air Raiders (1)" +"Cartridge.Manufacturer" "M-Network" +"Cartridge.ModelNo" "MT5861" +"Display.YStart" "37" +"Display.Height" "239" +"" + +"Cartridge.MD5" "35be55426c1fec32dfb503b4f0651572" +"Cartridge.Name" "Air Raiders (2)" +"Cartridge.Manufacturer" "CCE" +"Cartridge.ModelNo" "C-817" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "239" +"" + +"Cartridge.MD5" "f1a0a23e6464d954e3a9579c4ccd01c8" +"Cartridge.Name" "Alien" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11006" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.XStart" "16" +"Display.Width" "128" +"Display.YStart" "39" +"Display.Height" "188" +"" + +"Cartridge.MD5" "103f1756d9dc0dd2b16b53ad0f0f1859" +"Cartridge.Name" "Alien's Return" +"Cartridge.Manufacturer" "ITT Family Games" +"Cartridge.ModelNo" "554-33391" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"Display.Height" "214" +"" + +"Cartridge.MD5" "9e01f7f95cb8596765e03b9a36e8e33c" +"Cartridge.Name" "Alpha Beam with Ernie" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26103" +"Cartridge.Rarity" "Rare" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"Display.Height" "195" +"" + +"Cartridge.MD5" "acb7750b4d0c4bd34969802a7deb2990" +"Cartridge.Name" "Amidar" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5310" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Cartridge.Note" "One player joystick only" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "40" +"Display.Height" "182" +"Console.LeftDifficulty" "A" +"Console.RightDifficulty" "A" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "038e1e79c3d4410defde4bfe0b99cc32" +"Cartridge.Name" "Aquaventure" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Cartridge.Type" "F8" +"Emulation.HmoveBlanks" "No" +"Display.FrameRate" "60" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "a7b584937911d60c120677fe0d47f36f" +"Cartridge.Name" "Armor Ambush" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5661" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "c77c35a6fc3c0f12bf9e8bae48cba54b" +"Cartridge.Name" "Artillery Duel (1)" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99004" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "186" +"" + +"Cartridge.MD5" "3f039981255691d3859d04ef813a1264" +"Cartridge.Name" "Artillery Duel (2)" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99004" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "186" +"" + +"Cartridge.MD5" "de78b3a064d374390ac0710f95edde92" +"Cartridge.Name" "Assault" +"Cartridge.Manufacturer" "Bomb" +"Cartridge.ModelNo" "CA281" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "34" +"Display.Height" "203" +"" + +"Cartridge.MD5" "c5c7cc66febf2d4e743b4459de7ed868" +"Cartridge.Name" "Asterix" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2696" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"Display.Height" "187" +"" + +"Cartridge.MD5" "b227175699e372b8fe10ce243ad6dda5" +"Cartridge.Name" "Asteroids (Copyright Version)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2649 / 4975163" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "ccbd36746ed4525821a8083b0d6d2c2c" +"Cartridge.Name" "Asteroids" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2649" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "75169c08b56e4e6c36681e599c4d8cc5" +"Cartridge.Name" "Astroblast" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5666" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Uses left swapped paddles" +"Controller.Left" "Joystick" +"Controller.Right" "None" +"Display.YStart" "27" +"Display.Height" "202" +"" + +"Cartridge.MD5" "8f53a3b925f0fd961d9b8c4d46ee6755" +"Cartridge.Name" "Astro War" +"Cartridge.Manufacturer" "Artic" +"Cartridge.ModelNo" "SM8002" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.Height" "202" +"" + +"Cartridge.MD5" "3f540a30fdee0b20aed7288e4a5ea528" +"Cartridge.Name" "Atari Video Cube" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2670" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.YStart" "38" +"Display.Height" "200" +"" + +"Cartridge.MD5" "9ad36e699ef6f45d9eb6c4cf90475c9f" +"Cartridge.Name" "Atlantis" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3203" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "20" +"Display.YStart" "49" +"Display.Height" "185" +"" + +"Cartridge.MD5" "5b124850de9eea66781a50b2e9837000" +"Cartridge.Name" "Bachelor Party" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.ModelNo" "1002" +"Cartridge.Rarity" "Extremely Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"Display.YStart" "27" +"Display.Height" "219" +"" + +"Cartridge.MD5" "274d17ccd825ef9c728d68394b4569d2" +"Cartridge.Name" "Bachelorette Party" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.ModelNo" "1004" +"Cartridge.Rarity" "Extremely Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"Display.YStart" "27" +"Display.Height" "219" +"" + +"Cartridge.MD5" "8556b42aa05f94bc29ff39c39b11bff4" +"Cartridge.Name" "Backgammon" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2617 / 6699848 / 4975183" +"Cartridge.Rarity" "Extremely Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "00ce0bdd43aed84a983bef38fe7f5ee3" +"Cartridge.Name" "Bank Heist" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11012" +"Cartridge.Rarity" "Rare" +"Display.Height" "187" +"" + +"Cartridge.MD5" "f8240e62d8c0a64a61e19388414e3104" +"Cartridge.Name" "Barnstorming" +"Cartridge.Manufacturer" "Activision (Steve Cartwright)" +"Cartridge.ModelNo" "AX-013" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "42" +"Display.Height" "190" +"" + +"Cartridge.MD5" "819aeeb9a2e11deb54e6de334f843894" +"Cartridge.Name" "Basic Math / Fun With Numbers" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2661" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Released under two titles" +"Display.YStart" "22" +"" + +"Cartridge.MD5" "9f48eeb47836cf145a15771775f0767a" +"Cartridge.Name" "Basic Programming" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX262" +"Cartridge.Rarity" "Rare" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"Display.Height" "220" +"" + +"Cartridge.MD5" "d6dc9b4508da407e2437bfa4de53d1b2" +"Cartridge.Name" "Base Attack" +"Cartridge.Manufacturer" "Homevision" +"Cartridge.ModelNo" "13" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"Display.Height" "205" +"" + +"Cartridge.MD5" "ab4ac994865fb16ebb85738316309457" +"Cartridge.Name" "Basketball" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2624" +"Cartridge.Rarity" "Common" +"Display.YStart" "42" +"Display.Height" "193" +"" + +"Cartridge.MD5" "e13c7627b2e136b9c449d9e8925b4547" +"Cartridge.Name" "Basketball (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2624" +"Cartridge.Rarity" "Common" +"Display.YStart" "42" +"Display.Height" "193" +"" + +"Cartridge.MD5" "41f252a66c6301f1e8ab3612c19bc5d4" +"Cartridge.Name" "Battlezone" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2681" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F8" +"Emulation.HmoveBlanks" "No" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "32" +"Display.Height" "200" +"" + +"Cartridge.Name" "Beamrider (1)" +"Cartridge.MD5" "3604e725e81dd0abede07fd1c82eb058" +"Cartridge.Manufacturer" "Activision (Dave Rolle)" +"Cartridge.ModelNo" "AZ-037-04" +"Cartridge.Rarity" "Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "79ab4123a83dc11d468fb2108ea09e2e" +"Cartridge.Name" "Beamrider (2)" +"Cartridge.Manufacturer" "Activision (Dave Rolle)" +"Cartridge.ModelNo" "AZ-037-04" +"Cartridge.Rarity" "Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "d0b9df57bfea66378c0418ec68cfe37f" +"Cartridge.Name" "Beany Bopper" +"Cartridge.Manufacturer" "20th Century Fox Video Games (Sirius)" +"Cartridge.ModelNo" "11002" +"Cartridge.Rarity" "Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "ee6665683ebdb539e89ba620981cb0f6" +"Cartridge.Name" "Bearenstein Bears" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2658" +"Cartridge.Rarity" "Unbelieveably Rare" +"Cartridge.Type" "F8" +"" + +"Cartridge.MD5" "59e96de9628e8373d1c685f5e57dcf10" +"Cartridge.Name" "Beat 'Em and Eat 'Em" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.ModelNo" "1003" +"Cartridge.Rarity" "Extremely Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "073d7aff37b7601431e4f742c36c0dc1" +"Cartridge.Name" "Bermuda" +"Cartridge.Manufacturer" "Rainbow Vision" +"Cartridge.ModelNo" "SS-009" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "30" +"Display.Height" "199" +"" + +"Cartridge.MD5" "b8ed78afdb1e6cfe44ef6e3428789d5f" +"Cartridge.Name" "Bermuda Triangle" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "112-007" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "136f75c4dd02c29283752b7e5799f978" +"Cartridge.Name" "Berzerk" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2650 / 4975168" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.FrameRate" "20" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "38" +"Display.Height" "188" +"" + +"Cartridge.MD5" "1278f74ca1dfaa9122df3eca3c5bcaad" +"Cartridge.Name" "Bi! Bi!" +"Cartridge.Manufacturer" "Rainbow Vision" +"Cartridge.Note" "Variant of Sea Hunt by Panda" +"Cartridge.ModelNo" "SS-013" +"Display.YStart" "42" +"Display.Height" "245" +"" + +"Cartridge.MD5" "1802cc46b879b229272501998c5de04f" +"Cartridge.Name" "Big Bird's Egg Catch" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26104" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "One player left keypad only" +"Display.YStart" "27" +"Controller.Left" "Keypad" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "0a981c03204ac2b278ba392674682560" +"Cartridge.Name" "Blackjack" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2651 / 6699805 / 4975602" +"Cartridge.Rarity" "Rare" +"Display.YStart" "34" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "33d68c3cd74e5bc4cf0df3716c5848bc" +"Cartridge.Name" "Blueprint" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "4L-2486" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "968efc79d500dce52a906870a97358ab" +"Cartridge.Name" "BMX Airmaster" +"Cartridge.Manufacturer" "Atari / TNT Games" +"Cartridge.ModelNo" "CX26190" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "2823364702595feea24a3fbee138a243" +"Cartridge.Name" "Bobby Is Going Home" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG206" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "245" +"" + +"Cartridge.MD5" "c59633dbebd926c150fb6d30b0576405" +"Cartridge.Name" "Bogey Blaster" +"Cartridge.Manufacturer" "Telegames" +"Cartridge.ModelNo" "5861 A030" +"Cartridge.Rarity" "Rare" +"Display.Height" "200" +"" + +"Cartridge.MD5" "14c2548712099c220964d7f044c59fd9" +"Cartridge.Name" "Boing" +"Cartridge.Manufacturer" "First Star Software" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.YStart" "40" +"Display.Height" "184" +"" + +"Cartridge.MD5" "a2aae759e4e76f85c8afec3b86529317" +"Cartridge.Name" "Boom Bang" +"Cartridge.Manufacturer" "Cooper Black" +"Cartridge.Note" "Variant of Crackpots by Activision" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "c9b7afad3bfd922e006a6bfc1d4f3fe7" +"Cartridge.Name" "Bowling" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2628 / 6699842 / 4975117" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "d4aa6d6095258ce46aaf6f144b09eea7" +"Cartridge.Name" "Bowling (non-functional Taiwanese clone)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2628 / 6699842 / 4975117" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "a28d872fc50fa6b64eb35981d0f4bb8d" +"Cartridge.Name" "Bowling (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2628 / 6699842 / 4975117" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "c3ef5c4653212088eda54dc91d787870" +"Cartridge.Name" "Boxing" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AG-002" +"Cartridge.Rarity" "Uncommon" +"Display.XStart" "8" +"Display.Width" "140" +"" + +"Cartridge.MD5" "1cca2197d95c5a41f2add49a13738055" +"Cartridge.Name" "Brain Games" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2664 / 6699818" +"Cartridge.Rarity" "Uncommon" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"" + +"Cartridge.MD5" "f34f08e5eb96e500e851a80be3277a56" +"Cartridge.Name" "Breakout / Breakaway IV" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2622" +"Cartridge.Rarity" "Common" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "e80a4026d29777c3c7993fbfaee8920f" +"Cartridge.Name" "Brick Kick" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "cfd6a8b23d12b0462baf6a05ef347cd8" +"Cartridge.Name" "Bridge" +"Cartridge.Manufacturer" "Activision (Larry Kaplan)" +"Cartridge.ModelNo" "AX-006" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "1cf59fc7b11cdbcefe931e41641772f6" +"Cartridge.Name" "Buck Rogers-Planet of Zoom" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "005-01" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "fa4404fabc094e3a31fcd7b559cdd029" +"Cartridge.Name" "Bugs Bunny" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.Height" "205" +"" + +"Cartridge.MD5" "68597264c8e57ada93be3a5be4565096" +"Cartridge.Name" "Bugs" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "DA 1005" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "76f53abbbf39a0063f24036d6ee0968a" +"Cartridge.Name" "Bump 'N' Jump" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT7045" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "E7" +"Display.YStart" "41" +"Display.Height" "188" +"" + +"Cartridge.MD5" "0443cfa9872cdb49069186413275fa21" +"Cartridge.Name" "Burgertime" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT4518" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "E7" +"Display.YStart" "31" +"Display.Height" "180" +"" + +"Cartridge.MD5" "19d6956ff17a959c48fcd8f4706a848d" +"Cartridge.Name" "Burning Desire" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "7f6533386644c7d6358f871666c86e79" +"Cartridge.Name" "Cakewalk" +"Cartridge.Manufacturer" "CommaVid" +"Cartridge.ModelNo" "CM-008" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "9ab72d3fd2cc1a0c9adb504502579037" +"Cartridge.Name" "California Games" +"Cartridge.Manufacturer" "Epyx" +"Cartridge.ModelNo" "8056100286" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.FrameRate" "20" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "43" +"Display.Height" "192" +"" + +"Cartridge.MD5" "3051b6071cb26377cd428af155e1bfc4" +"Cartridge.Name" "Canyon Bomber (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2607 / 6699828 / 4975115" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "47" +"Display.Height" "195" +"" + +"Cartridge.MD5" "feedcc20bc3ca34851cd5d9e38aa2ca6" +"Cartridge.Name" "Canyon Bomber" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2607 / 6699828 / 4975115" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "47" +"Display.Height" "195" +"" + +"Cartridge.MD5" "028024fb8e5e5f18ea586652f9799c96" +"Cartridge.Name" "Carnival" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2468" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"Display.Height" "212" +"" + +"Cartridge.MD5" "b816296311019ab69a21cb9e9e235d12" +"Cartridge.Name" "Casino / Poker Plus" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2652" +"Cartridge.Rarity" "Uncommon" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "76f66ce3b83d7a104a899b4b3354a2f2" +"Cartridge.Name" "Cat & Mouse" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.YStart" "33" +"Display.Height" "200" +"" + +"Cartridge.MD5" "9e192601829f5f5c2d3b51f8ae25dbe5" +"Cartridge.Name" "Cathouse Blues" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "91c2098e88a6b13f977af8c003e0bca5" +"Cartridge.Name" "Centipede" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2676" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "39" +"Display.Height" "192" +"" + +"Cartridge.MD5" "4311a4115fb7bc68477c96cf44cebacf" +"Cartridge.Name" "Challenge (1)" +"Cartridge.Manufacturer" "Zellers" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "213" +"" + +"Cartridge.MD5" "9905f9f4706223dadee84f6867ede8e3" +"Cartridge.Name" "Challenge (2)" +"Cartridge.Manufacturer" "Zellers" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "213" +"" + +"Cartridge.MD5" "5d799bfa9e1e7b6224877162accada0d" +"Cartridge.Name" "Challenge of...NEXAR" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-206" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "36" +"Display.Height" "195" +"" + +"Cartridge.MD5" "3e33ac10dcf2dff014bc1decf8a9aea4" +"Cartridge.Name" "Chase The Chuckwagon" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.YStart" "22" +"" + +"Cartridge.MD5" "3f5a43602f960ede330cd2f43a25139e" +"Cartridge.Name" "Checkers" +"Cartridge.Manufacturer" "Activision (Alan Miller)" +"Cartridge.ModelNo" "AG-003" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.XStart" "8" +"" + +"Cartridge.MD5" "749fec9918160921576f850b2375b516" +"Cartridge.Name" "China Syndrome" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-205" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.YStart" "32" +"Display.Height" "207" +"" + +"Cartridge.MD5" "c1cb228470a87beb5f36e90ac745da26" +"Cartridge.Name" "Chopper Command" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AX-015" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "49" +"Display.Height" "183" +"" + +"Cartridge.MD5" "3f58f972276d1e4e0e09582521ed7a5b" +"Cartridge.Name" "Chuck Norris Superkicks / Kung Fu Superkicks / Superkicks" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99003" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "40" +"Display.Height" "195" +"" + +"Cartridge.MD5" "a29df35557f31dfea2e2ae4609c6ebb7" +"Cartridge.Name" "Circus Atari / Circus (Joystick Version)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2630 / 4975122" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "42" +"Display.Height" "190" +"" + +"Cartridge.MD5" "a7b96a8150600b3e800a4689c3ec60a2" +"Cartridge.Name" "Circus Atari / Circus (Paddle Version)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2630 / 4975122" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Cartridge.Note" "Two player left paddles only" +"Display.YStart" "42" +"Display.Height" "190" +"Controller.Left" "Paddles" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "93acd5020ae8eb5673601e2edecbc158" +"Cartridge.Name" "Clock (version 3)" +"" + +"Cartridge.MD5" "7ff53f6922708119e7bf478d7d618c86" +"Cartridge.Name" "Clown Down Town" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "219" +"" + +"Cartridge.MD5" "1e587ca91518a47753a28217cd4fd586" +"Cartridge.Name" "Coconuts" +"Cartridge.Manufacturer" "Telesys" +"Cartridge.ModelNo" "1001" +"Cartridge.Rarity" "Rare" +"Display.YStart" "37" +"" + +"Cartridge.MD5" "5846b1d34c296bf7afc2fa05bbc16e98" +"Cartridge.Name" "Code Breaker" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2643" +"Cartridge.Rarity" "Uncommon" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"Display.YStart" "33" +"" + +"Cartridge.MD5" "212d0b200ed8b45d8795ad899734d7d7" +"Cartridge.Name" "Coke Wins" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"" + +"Cartridge.MD5" "4093382187f8387e6d011883e8ea519b" +"Cartridge.Name" "Col 'N'" +"Cartridge.Manufacturer" "ITT Family Games" +"Cartridge.ModelNo" "554-33391" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"Display.Height" "214" +"" + +"Cartridge.MD5" "ac05c0e53a5e7009ddd75ed4b99949fc" +"Cartridge.Name" "Combat / Tank Plus (1)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2601 / 6699801 / 4975124" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "2K" +"Display.YStart" "39" +"Display.Height" "206" +"" + +"Cartridge.MD5" "4c8832ed387bbafc055320c05205bc08" +"Cartridge.Name" "Combat / Tank Plus (2)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2601 / 6699801 / 4975124" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "2K" +"Display.YStart" "39" +"Display.Height" "206" +"" + +"Cartridge.MD5" "be35d8b37bbc03848a5f020662a99909" +"Cartridge.Name" "Combat / Tank Plus (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2601 / 6699801 / 4975124" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "2K" +"Display.YStart" "39" +"Display.Height" "206" +"" + +"Cartridge.MD5" "76a9bf05a6de8418a3ebc7fc254b71b4" +"Cartridge.Name" "Color Bar Generator" +"Cartridge.Manufacturer" "VideoSoft" +"Cartridge.ModelNo" "VS1008" +"Cartridge.Rarity" "Unreleased" +"" + +"Cartridge.MD5" "5d2cc33ca798783dee435eb29debf6d6" +"Cartridge.Name" "Commando" +"Cartridge.Manufacturer" "Activision (Mike Reidel)" +"Cartridge.ModelNo" "AK-043" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "47" +"Display.Height" "180" +"" + +"Cartridge.MD5" "f457674cef449cfd85f21db2b4f631a7" +"Cartridge.Name" "Commando Raid" +"Cartridge.Manufacturer" "U.S. Games (Vidtec)" +"Cartridge.ModelNo" "VC 1004" +"Cartridge.Rarity" "Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "2c8835aed7f52a0da9ade5226ee5aa75" +"Cartridge.Name" "Communist Mutants from Space" +"Cartridge.ModelNo" "AR-4101" +"Cartridge.Manufacturer" "Starpath (Steve Landrum)" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "38" +"Display.Height" "196" +"" + +"Cartridge.MD5" "1f21666b8f78b65051b7a609f1d48608" +"Cartridge.Name" "Condor Attack" +"Cartridge.Manufacturer" "CCE" +"Cartridge.ModelNo" "C-851" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "00b7b4cbec81570642283e7fc1ef17af" +"Cartridge.Name" "Congo Bongo" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "006-01" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "57c5b351d4de021785cf8ed8191a195c" +"Cartridge.Name" "Cookie Monster Munch" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26102" +"Cartridge.Rarity" "Rare" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"Display.YStart" "36" +"Display.Height" "190" +"" + +"Cartridge.MD5" "ab5bf1ef5e463ad1cbb11b6a33797228" +"Cartridge.Name" "Cosmic Ark" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3204" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.YStart" "42" +"Display.Height" "192" +"" + +"Cartridge.MD5" "133b56de011d562cbab665968bde352b" +"Cartridge.Name" "Cosmic Commuter" +"Cartridge.Manufacturer" "Activision (John van Ryzin)" +"Cartridge.ModelNo" "AG-038" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "60" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "f367e58667a30e7482175809e3cec4d4" +"Cartridge.Name" "Cosmic Corridor" +"Cartridge.Manufacturer" "Emag / Zimag" +"Cartridge.ModelNo" "GN-040 / 708-111" +"Cartridge.Rarity" "Rare" +"Display.YStart" "35" +"Display.Height" "212" +"" + +"Cartridge.MD5" "3c853d864a1d5534ed0d4b325347f131" +"Cartridge.Name" "Cosmic Creeps" +"Cartridge.Manufacturer" "Telesys" +"Cartridge.ModelNo" "1002" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "e5f17b3e62a21d0df1ca9aee1aa8c7c5" +"Cartridge.Name" "Cosmic Swarm" +"Cartridge.Manufacturer" "CommaVid" +"Cartridge.ModelNo" "CM-003" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "9dec0be14d899e1aac4337acef5ab94a" +"Cartridge.Name" "Cosmic Swarm (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "CommaVid" +"Cartridge.ModelNo" "CM-003" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "a184846d8904396830951217b47d13d9" +"Cartridge.Name" "Crackpots" +"Cartridge.Manufacturer" "Activision (Dan Kitchen)" +"Cartridge.ModelNo" "AX-029" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "fb88c400d602fe759ae74ef1716ee84e" +"Cartridge.Name" "Crash Dive" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11031" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "55ef7b65066428367844342ed59f956c" +"Cartridge.Name" "Crazy Climber" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2683" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "46" +"Display.Height" "195" +"" + +"Cartridge.MD5" "48f18d69799a5f5451a5f0d17876acef" +"Cartridge.Name" "Criminal Pursuit" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "8cd26dcf249456fe4aeb8db42d49df74" +"Cartridge.Name" "Crossbow" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26139" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "c17bdc7d14a36e10837d039f43ee5fa3" +"Cartridge.Name" "Cross Force" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-203" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "74f623833429d35341b7a84bc09793c0" +"Cartridge.Name" "Cruise Missile" +"Cartridge.Manufacturer" "Froggo" +"Cartridge.ModelNo" "FG 1007" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "384f5fbf57b5e92ed708935ebf8a8610" +"Cartridge.Name" "Crypts of Chaos" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11009" +"Cartridge.Rarity" "Rare" +"Display.YStart" "52" +"Display.Height" "185" +"" + +"Cartridge.MD5" "1c6eb740d3c485766cade566abab8208" +"Cartridge.Name" "Crystal Castles" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26110" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6SC" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "50" +"Display.Height" "174" +"" + +"Cartridge.MD5" "6fa0ac6943e33637d8e77df14962fbfc" +"Cartridge.Name" "Cubicolor" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "6caff1b4b96ed098123febf51f9121dc" +"Cartridge.Name" "Cubis (PAL version)" +"Cartridge.Manufacturer" "Eckhard Stolberg" +"Cartridge.Rarity" "New Release" +"Display.Format" "PAL" +"" + +"Cartridge.MD5" "58513bae774360b96866a07ca0e8fd8e" +"Cartridge.Name" "Custer's Revenge" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.ModelNo" "1001" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "929e8a84ed50601d9af8c49b0425c7ea" +"Cartridge.Name" "Dancing Plate" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG205" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "230" +"" + +"Cartridge.MD5" "a422194290c64ef9d444da9d6a207807" +"Cartridge.Name" "Dark Cavern" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5667" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.YStart" "41" +"Display.Height" "189" +"" + +"Cartridge.MD5" "106855474c69d08c8ffa308d47337269" +"Cartridge.Name" "Dark Chambers" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26151" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6SC" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "48" +"Display.Height" "170" +"" + +"Cartridge.MD5" "dba270850ae997969a18ee0001675821" +"Cartridge.Name" "Dark Mage (4K version)" +"Cartridge.Manufacturer" "Greg Troutman" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "6333ef5b5cbb77acd47f558c8b7a95d3" +"Cartridge.Name" "Dark Mage (8K version)" +"Cartridge.Manufacturer" "Greg Troutman" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "e4c00beb17fdc5881757855f2838c816" +"Cartridge.Name" "Deadly Duck" +"Cartridge.Manufacturer" "20th Century Fox Video Games (Sirius)" +"Cartridge.ModelNo" "11004" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "4e15ddfd48bca4f0bf999240c47b49f5" +"Cartridge.Name" "Deathtrap" +"Cartridge.Manufacturer" "Avalon Hill" +"Cartridge.ModelNo" "50010" +"Cartridge.Rarity" "Unbelievably Rare" +"" + +"Cartridge.MD5" "ac7c2260378975614192ca2bc3d20e0b" +"Cartridge.Name" "Decathlon" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AZ-030" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "FE" +"" + +"Cartridge.MD5" "0f643c34e40e3f1daafd9c524d3ffe64" +"Cartridge.Name" "Defender" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2609 / 4975186" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "3a771876e4b61d42e3a3892ad885d889" +"Cartridge.Name" "Defender II" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26120" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "Variant of Stargate by Atari" +"Cartridge.Type" "F8SC" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "37" +"Display.Height" "190" +"" + +"Cartridge.MD5" "d09935802d6760ae58253685ff649268" +"Cartridge.Name" "Demolition Herby" +"Cartridge.Manufacturer" "Telesys" +"Cartridge.ModelNo" "1006" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "187" +"" + +"Cartridge.MD5" "f0e0addc07971561ab80d9abe1b8d333" +"Cartridge.Name" "Demon Attack" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3200" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "39" +"Display.Height" "194" +"" + +"Cartridge.MD5" "698f569eab5a9906eec3bc7c6b3e0980" +"Cartridge.Name" "Demons!" +"Cartridge.Manufacturer" "SpkSoft" +"Cartridge.Note" "Variant of Phoenix by Atari and Demon Attack by Imagic" +"" + +"Cartridge.MD5" "f91fb8da3223b79f1c9a07b77ebfa0b2" +"Cartridge.Name" "Demons to Diamonds" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2615 / 4975140" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Two player left paddles only (swapped)" +"Display.YStart" "35" +"Display.Height" "195" +"Controller.Left" "Paddles" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "fd4f5536fd80f35c64d365df85873418" +"Cartridge.Name" "Desert Falcon" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26140" +"Cartridge.Rarity" "Common" +"Display.YStart" "17" +"" + +"Cartridge.MD5" "9222b25a0875022b412e8da37e7f6887" +"Cartridge.Name" "Dice Puzzle" +"Cartridge.Manufacturer" "Panda Inc." +"Cartridge.ModelNo" "106" +"Cartridge.Rarity" "Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "6dda84fb8e442ecf34241ac0d1d91d69" +"Cartridge.Name" "Dig Dug" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2677" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6SC" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "33" +"Display.Height" "193" +"" + +"Cartridge.MD5" "939ce554f5c0e74cc6e4e62810ec2111" +"Cartridge.Name" "Dishaster" +"Cartridge.Manufacturer" "Cooper Black" +"Cartridge.Note" "Variant of Dancing Plate by Emag/Zimag" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "83bdc819980db99bf89a7f2ed6a2de59" +"Cartridge.Name" "Dodge 'Em / Dodger Cars" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2637 / 4975158" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "57" +"Display.Height" "180" +"" + +"Cartridge.MD5" "ca09fa7406b7d2aea10d969b6fc90195" +"Cartridge.Name" "Dolphin" +"Cartridge.Manufacturer" "Activision (Matthew Hubbard)" +"Cartridge.ModelNo" "AX-024" +"Cartridge.Rarity" "Rare" +"Display.YStart" "40" +"Display.Height" "195" +"" + +"Cartridge.MD5" "84d1cf884f029e458db196548db9c2ad" +"Cartridge.Name" "Domino (PAL version)" +"Cartridge.Manufacturer" "Eckhard Stolberg" +"Cartridge.Rarity" "New Release" +"Display.Format" "PAL" +"" + +"Cartridge.MD5" "937736d899337036de818391a87271e0" +"Cartridge.Name" "Donald Duck's Speedboat" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26108" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "36b20c427975760cb9cf4a47e41369e4" +"Cartridge.Name" "Donkey Kong" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2451" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "7fd52208fb6391bae0cd7e68c27bde6f" +"Cartridge.Name" "Donkey Kong Jr. (1)" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2653" +"Cartridge.Rarity" "Rare" +"Display.YStart" "35" +"" + +"Cartridge.MD5" "c8fa5d69d9e555eb16068ef87b1c9c45" +"Cartridge.Name" "Donkey Kong Jr. (2)" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2653" +"Cartridge.Rarity" "Rare" +"Display.YStart" "35" +"" + +"Cartridge.MD5" "47464694e9cce07fdbfd096605bf39d4" +"Cartridge.Name" "Double Dragon" +"Cartridge.Manufacturer" "Activision (Dan Kitchen)" +"Cartridge.ModelNo" "AK-050-04" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "68" +"Display.Height" "190" +"" + +"Cartridge.MD5" "368d88a6c071caba60b4f778615aae94" +"Cartridge.Name" "Double Dunk" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26159" +"Cartridge.Rarity" "Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "6a882fb1413912d2ce5cf5fa62cf3875" +"Cartridge.Name" "Dragon Defender" +"Cartridge.ModelNo" "TP-605" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "32" +"Display.Height" "249" +"" + +"Cartridge.MD5" "41810dd94bd0de1110bedc5092bef5b0" +"Cartridge.Name" "Dragonfire" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3611" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "38" +"Display.Height" "191" +"" + +"Cartridge.MD5" "90ccf4f30a5ad8c801090b388ddd5613" +"Cartridge.Name" "Dragonstomper" +"Cartridge.ModelNo" "AR-4400" +"Cartridge.Manufacturer" "Starpath (Steve Landrum)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "39" +"Display.Height" "189" +"" + +"Cartridge.MD5" "77057d9d14b99e465ea9e29783af0ae3" +"Cartridge.Name" "Dragster" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AG-001" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "1bb91bae919ddbd655fa25c54ea6f532" +"Cartridge.Name" "Duck Shoot" +"Display.YStart" "67" +"Display.Height" "187" +"" + +"Cartridge.MD5" "1f773a94d919b2a3c647172bbb97f6b4" +"Cartridge.Name" "Dumbo's Flying Circus" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2678" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"" + +"Cartridge.MD5" "51de328e79d919d7234cf19c1cd77fbc" +"Cartridge.Name" "Dukes of Hazard" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2678" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"" + +"Cartridge.MD5" "033e21521e0bf4e54e8816873943406d" +"Cartridge.Name" "Earth Dies Screaming, The" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11020" +"Cartridge.Rarity" "Rare" +"Display.YStart" "30" +"Display.Height" "205" +"" + +"Cartridge.MD5" "42b2c3b4545f1499a083cfbc4a3b7640" +"Cartridge.Name" "Eggomania" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2003" +"Cartridge.Rarity" "Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "b6812eaf87127f043e78f91f2028f9f4" +"Cartridge.Name" "Eli's Ladder" +"Cartridge.Manufacturer" "Simage" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "7eafc9827e8d5b1336905939e097aae7" +"Cartridge.Name" "Elk Attack" +"Cartridge.Manufacturer" "Mark R. Hahn" +"Cartridge.Rarity" "Uncommon / New Release" +"Cartridge.Type" "F8" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "37" +"Display.Height" "194" +"" + +"Cartridge.MD5" "dbc8829ef6f12db8f463e30f60af209f" +"Cartridge.Name" "Encounter at L-5" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "DA 1001" +"Cartridge.Rarity" "Uncommon" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "4279485e922b34f127a88904b31ce9fa" +"Cartridge.Name" "Enduro (1)" +"Cartridge.Manufacturer" "Activision (Larry Miller)" +"Cartridge.ModelNo" "AX-026" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "20" +"Display.XStart" "12" +"Display.Width" "140" +"Display.YStart" "64" +"Display.Height" "175" +"" + +"Cartridge.MD5" "94b92a882f6dbaa6993a46e2dcc58402" +"Cartridge.Name" "Enduro (2)" +"Cartridge.Manufacturer" "Activision (Larry Miller)" +"Cartridge.ModelNo" "AX-026" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "20" +"Display.XStart" "12" +"Display.Width" "140" +"Display.YStart" "64" +"Display.Height" "175" +"" + +"Cartridge.MD5" "6b683be69f92958abe0e2a9945157ad5" +"Cartridge.Name" "Entombed / Name That Game" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2007 / VC 1007" +"Cartridge.Rarity" "Extremely Rare / Rare" +"Cartridge.Note" "Released as Name That Game for a contest (winning name was Entombed)" +"" + +"Cartridge.MD5" "d1a1841b7f2007a24439ac248374630a" +"Cartridge.Name" "Escape from the Mindmaster" +"Cartridge.ModelNo" "AR-4200" +"Cartridge.Manufacturer" "Starpath (Dennis Caswell)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "36" +"Display.Height" "192" +"Emulation.HmoveBlanks" "No" +"" + +"Cartridge.MD5" "f1127ade54037236e75a133b1dfc389d" +"Cartridge.Name" "Escape from the Mindmaster - Demonstration" +"Cartridge.ModelNo" "AR-4200" +"Cartridge.Manufacturer" "Starpath (Dennis Caswell)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "36" +"Display.Height" "192" +"" + +"Cartridge.MD5" "f7a138eed69665b5cd1bfa796a550b01" +"Cartridge.Name" "Espial" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-012" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "3F" +"Display.YStart" "76" +"Display.Height" "196" + +"" + +"Cartridge.MD5" "615a3bf251a38eb6638cdc7ffbde5480" +"Cartridge.Name" "E.T.-The Extra Terrestrial" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2674" +"Cartridge.Rarity" "Common" +"Display.Height" "190" +"" + +"Cartridge.MD5" "6362396c8344eec3e86731a700b13abf" +"Cartridge.Name" "Exocet" +"Cartridge.Manufacturer" "Panda Inc." +"Cartridge.ModelNo" "109" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "2ac3a08cfbf1942ba169c3e9e6c47e09" +"Cartridge.Name" "F14 Tomcat Flight Simulator" +"Cartridge.Manufacturer" "Absolute Entertainment" +"Cartridge.ModelNo" "AK-046" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "F6" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "56" +"Display.Height" "196" +"" + +"Cartridge.MD5" "b80d50ecee73919a507498d0a4d922ae" +"Cartridge.Name" "Fantastic Voyage" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11008" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"Display.Height" "200" +"" + +"Cartridge.MD5" "f7e07080ed8396b68f2e5788a5c245e2" +"Cartridge.Name" "Farmyard Fun" +"Cartridge.ModelNo" "TP-617" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "212" +"" + +"Cartridge.MD5" "9de0d45731f90a0a922ab09228510393" +"Cartridge.Name" "Fast Eddie" +"Cartridge.Manufacturer" "20th Century Fox Video Games (Sirius)" +"Cartridge.ModelNo" "11003" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.YStart" "38" +"Display.Height" "198" +"" + +"Cartridge.MD5" "665b8f8ead0eef220ed53886fbd61ec9" +"Cartridge.Name" "Fast Food" +"Cartridge.Manufacturer" "Telesys" +"Cartridge.ModelNo" "1003" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "074ec425ec20579e64a7ded592155d48" +"Cartridge.Name" "Fatal Run" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26162" +"Cartridge.Rarity" "Unbelievably Rare" +"Cartridge.Type" "F4SC" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "70" +"Display.Height" "178" +"" + +"Cartridge.MD5" "0b55399cf640a2a00ba72dd155a0c140" +"Cartridge.Name" "Fathom" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "O3205" +"Cartridge.Rarity" "Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "211fbbdbbca1102dc5b43dc8157c09b3" +"Cartridge.Name" "Final Approach" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2009" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "386ff28ac5e254ba1b1bac6916bcc93a" +"Cartridge.Name" "Fireball" +"Cartridge.ModelNo" "AR-4300" +"Cartridge.Manufacturer" "Starpath (Scott Nelson)" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "136" +"Display.YStart" "32" +"Display.Height" "200" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "d09f1830fb316515b90694c45728d702" +"Cartridge.Name" "Fire Fighter" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3400" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "20dca534b997bf607d658e77fbb3c0ee" +"Cartridge.Name" "Fire Fly" +"Cartridge.Manufacturer" "Mythicon" +"Cartridge.ModelNo" "MA-1002" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "d3171407c3a8bb401a3a62eb578f48fb" +"Cartridge.Name" "Fire Spinner" +"Cartridge.Manufacturer" "Emag" +"Cartridge.ModelNo" "GN-080" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "01e60a109a6a67c70d3c0528381d0187" +"Cartridge.Name" "Fire Birds" +"Cartridge.Manufacturer" "ITT Family Games" +"Cartridge.ModelNo" "554-33383" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"" + +"Cartridge.MD5" "b8865f05676e64f3bec72b9defdacfa7" +"Cartridge.Name" "Fishing Derby" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AG-004" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "200" +"" + +"Cartridge.MD5" "30512e0e83903fc05541d2f6a6a62654" +"Cartridge.Name" "Flag Capture" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2644" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "39" +"" + +"Cartridge.MD5" "8786c1e56ef221d946c64f6b65b697e9" +"Cartridge.Name" "Flash Gordon" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11015" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "e549f1178e038fa88dc6d657dc441146" +"Cartridge.Name" "Football" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2625 / 6699827 / 4975114" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "213e5e82ecb42af237cfed8612c128ac" +"Cartridge.Name" "Forest" +"Cartridge.Manufacturer" "Panda Inc." +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "34" +"Display.Height" "254" +"" + +"Cartridge.MD5" "15dd21c2608e0d7d9f54c0d3f08cca1f" +"Cartridge.Name" "Frankenstein's Monster" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "112-008" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "8e0ab801b1705a740b476b7f588c6d16" +"Cartridge.Name" "Freeway" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AG-009" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "2K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "38" +"Display.Height" "200" +"" + +"Cartridge.MD5" "5f73e7175474c1c22fb8030c3158e9b3" +"Cartridge.Name" "Frog Pond" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2665" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.YStart" "37" +"Display.Height" "190" +"" + +"Cartridge.MD5" "081e2c114c9c20b61acf25fc95c71bf4" +"Cartridge.Name" "Frogger" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5300" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "34" +"Display.Height" "191" +"" + +"Cartridge.MD5" "27c6a2ca16ad7d814626ceea62fa8fb4" +"Cartridge.Name" "Frogger II: Threedeep!" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5590" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "E0" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "40" +"Display.Height" "194" +"" + +"Cartridge.MD5" "c73ae5ba5a0a3f3ac77f0a9e14770e73" +"Cartridge.Name" "Official Frogger by Sega, The" +"Cartridge.ModelNo" "AR-4105" +"Cartridge.Manufacturer" "Starpath (Steve Landrum)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "33" +"Display.Height" "205" +"" + +"Cartridge.MD5" "f5d103a9ae36d1d4ee7eef657b75d2b3" +"Cartridge.Name" "Official Frogger by Sega, The - Demonstration" +"Cartridge.ModelNo" "AR-4105" +"Cartridge.Manufacturer" "Starpath (Steve Landrum)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "33" +"Display.Height" "205" +"" + +"Cartridge.MD5" "dcc2956c7a39fdbf1e861fc5c595da0d" +"Cartridge.Name" "Frogs and Flies" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5664" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "42" +"Display.Height" "194" +"" + +"Cartridge.MD5" "e556e07cc06c803f2955986f53ef63ed" +"Cartridge.Name" "Front Line" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2665" +"Cartridge.Rarity" "Rare" +"Display.Height" "192" +"" + +"Cartridge.MD5" "4ca73eb959299471788f0b685c3ba0b5" +"Cartridge.Name" "Frostbite" +"Cartridge.Manufacturer" "Activision (Steve Cartwright)" +"Cartridge.ModelNo" "AX-031" +"Cartridge.Rarity" "Rare" +"Display.Height" "197" +"" + +"Cartridge.MD5" "01b09872dcd9556427761f0ed64aa42a" +"Cartridge.Name" "Galaga" +"Cartridge.Note" "Variant of River Raid by Activision (Carol Shaw)" +"Cartridge.Type" "4K" +"Display.YStart" "36" +"Display.Height" "199" +"" + +"Cartridge.MD5" "211774f4c5739042618be8ff67351177" +"Cartridge.Name" "Galaxian" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2684" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F8" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "36" +"Display.Height" "194" +"" + +"Cartridge.MD5" "102672bbd7e25cd79f4384dd7214c32b" +"Cartridge.Name" "Game of Concentration, A / Hunt and Score / Memory Match" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2642 / 6699814" +"Cartridge.Rarity" "Rare (Uncommon) / Uncommon" +"Cartridge.Note" "Released under three titles" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"" + +"Cartridge.MD5" "20edcc3aa6c189259fa7e2f044a99c49" +"Cartridge.Name" "Gangster Alley" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-201" +"Cartridge.Rarity" "Rare" +"Display.Height" "200" +"" + +"Cartridge.MD5" "728152f5ae6fdd0d3a9b88709bee6c7a" +"Cartridge.Name" "Gas Hog" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-217" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "e64a8008812327853877a37befeb6465" +"Cartridge.Name" "Gauntlet" +"Cartridge.Manufacturer" "Answer Software" +"Cartridge.ModelNo" "ASC1002" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "e314b42761cd13c03def744b4afc7b1b" +"Cartridge.Name" "Ghostbusters" +"Cartridge.Manufacturer" "Activision (David Crane and Dan Kitchen)" +"Cartridge.ModelNo" "AZ-108-04" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "192" +"" + +"Cartridge.MD5" "0eecb5f58f55de9db4eedb3a0f6b74a8" +"Cartridge.Name" "Ghost Manor (1)" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99002" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "2bee7f226d506c217163bad4ab1768c0" +"Cartridge.Name" "Ghost Manor (2)" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99002" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "c1fdd44efda916414be3527a47752c75" +"Cartridge.Name" "G.I. Joe-Cobra Strike" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5920" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Uses paddle/joystick combination" +"Display.YStart" "27" +"Controller.Left" "Paddles" +"" + +"Cartridge.MD5" "1c8c42d1aee5010b30e7f1992d69216e" +"Cartridge.Name" "Gigilo" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.ModelNo" "1009" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "5e0c37f534ab5ccc4661768e2ddf0162" +"Cartridge.Name" "Glacier Patrol" +"Cartridge.Manufacturer" "Telegames (Sunrise)" +"Cartridge.ModelNo" "5667 A106" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "2d9e5d8d083b6367eda880e80dfdfaeb" +"Cartridge.Name" "Glib" +"Cartridge.Manufacturer" "QDI" +"Cartridge.ModelNo" "87" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "2e663eaa0d6b723b645e643750b942fd" +"Cartridge.Name" "Golf" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2634" +"Cartridge.Rarity" "Common" +"Display.YStart" "37" +"Display.Height" "190" +"" + +"Cartridge.MD5" "c16c79aad6272baffb8aae9a7fff0864" +"Cartridge.Name" "Gopher" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2001" +"Cartridge.Rarity" "Rare" +"Display.Height" "197" +"" + +"Cartridge.MD5" "81b3bf17cf01039d311b4cd738ae608e" +"Cartridge.Name" "Gorf" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "M8793" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "42" +"Display.Height" "190" +"" + +"Cartridge.MD5" "2903896d88a341511586d69fcfc20f7d" +"Cartridge.Name" "Grand Prix" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AX-014" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "46" +"Display.Height" "189" +"" + +"Cartridge.MD5" "8ac18076d01a6b63acf6e2cab4968940" +"Cartridge.Name" "Gravitar" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2685" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F8" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "18f299edb5ba709a64c80c8c9cec24f2" +"Cartridge.Name" "Great Escape" +"Cartridge.Manufacturer" "Bomb" +"Cartridge.ModelNo" "CA282" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "220" +"" + +"Cartridge.MD5" "01cb3e8dfab7203a9c62ba3b94b4e59f" +"Cartridge.Name" "Gremlins" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26127" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "4ac9f40ddfcf194bd8732a75b3f2f214" +"Cartridge.Name" "Grover's Music Maker" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26106" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Cartridge.Note" "One player left keypad only" +"Display.YStart" "27" +"Controller.Left" "Keypad" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "7ab2f190d4e59e8742e76a6e870b567e" +"Cartridge.Name" "Guardian" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2008" +"Cartridge.Rarity" "Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" +"Cartridge.MD5" "b311ab95e85bc0162308390728a7361d" +"Cartridge.Name" "Gyruss" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5080" +"Cartridge.Rarity" "Rare" +"Display.YStart" "47" +"Display.Height" "180" +"" + +"Cartridge.MD5" "30516cfbaa1bc3b5335ee53ad811f17a" +"Cartridge.Name" "Halloween" +"Cartridge.Manufacturer" "Wizard Video" +"Cartridge.ModelNo" "007" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "b9232c1de494875efe1858fc8390616d" +"Cartridge.Name" "Harbor Escape" +"Cartridge.Manufacturer" "Panda Inc" +"Cartridge.ModelNo" "110" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "f16c709df0a6c52f47ff52b9d95b7d8d" +"Cartridge.Name" "Hangman" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2662" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "f0a6e99f5875891246c3dbecbf2d2cea" +"Cartridge.Name" "Haunted House" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2654 / 4975141" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "38" +"Display.Height" "184" +"" + +"Cartridge.MD5" "fca4a5be1251927027f2c24774a02160" +"Cartridge.Name" "H.E.R.O." +"Cartridge.Manufacturer" "Activision (John van Ryzin)" +"Cartridge.ModelNo" "AZ-036-04" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F8" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "49" +"Display.Height" "190" +"" + +"Cartridge.MD5" "3d48b8b586a09bdbf49f1a016bf4d29a" +"Cartridge.Name" "Hole Hunter" +"Cartridge.ModelNo" "TP-606" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "220" +"" + +"Cartridge.MD5" "c52d9bbdc5530e1ef8e8ba7be692b01e" +"Cartridge.Name" "Holy Moley" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"" + +"Cartridge.MD5" "0bfabf1e98bdb180643f35f2165995d0" +"Cartridge.Name" "Home Run" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2623" +"Cartridge.Rarity" "Common" +"Display.YStart" "42" +"" + +"Cartridge.MD5" "7972e5101fa548b952d852db24ad6060" +"Cartridge.Name" "Human Cannonball / Cannon Man" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2627 / 6699841" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "f6a282374441012b01714e19699fc62a" +"Cartridge.Name" "I Want My Mommy" +"Cartridge.Manufacturer" "Emag / Zimag" +"Cartridge.ModelNo" "GN-010 / 710-111" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "a4c08c4994eb9d24fb78be1793e82e26" +"Cartridge.Name" "Ice Hockey" +"Cartridge.Manufacturer" "Activision (Alan Miller)" +"Cartridge.ModelNo" "AX-012" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "9813b9e4b8a6fd919c86a40c6bda8c93" +"Cartridge.Name" "Ikari Warriors" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26177" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "9b21d8fc78cc4308990d99a4d906ec52" +"Cartridge.Name" "Immies and Aggies" +"Cartridge.Manufacturer" "CCE" +"Cartridge.ModelNo" "C-838" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "3f6dbf448f25e2bd06dea44248eb122d" +"Cartridge.Name" "International Soccer" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5687" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "37" +"Display.Height" "195" +"" + +"Cartridge.MD5" "b4030c38a720dd84b84178b6ce1fc749" +"Cartridge.Name" "International Soccer (Pirate version) (1)" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5687" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "37" +"Display.Height" "195" +"" + +"Cartridge.MD5" "cd568d6acb2f14477ebf7e59fb382292" +"Cartridge.Name" "International Soccer (Pirate version) (2)" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5687" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "62" +"Display.Height" "195" +"" + +"Cartridge.MD5" "c5301f549d0722049bb0add6b10d1e09" +"Cartridge.Name" "Indy 500 / Race" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2611 / 6699821 / 4975149" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Uses two steering controllers" +"Controller.Left" "Driving" +"Controller.Right" "Driving" +"Display.YStart" "30" +"Display.Height" "205" +"" + +"Cartridge.MD5" "afe88aae81d99e0947c0cfb687b16251" +"Cartridge.Name" "Infiltrate" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2006" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "4868a81e1b6031ed66ecd60547e6ec85" +"Cartridge.Name" "Inv (1-3-98 version)" +"Cartridge.Manufacturer" "Eric Mooney and Piero Cavina" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "cd139ae6d09f3665ad09eb79da3f9e49" +"Cartridge.Name" "Inv (4-24-97 version)" +"Cartridge.Manufacturer" "Eric Mooney" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "4b9581c3100a1ef05eac1535d25385aa" +"Cartridge.Name" "IQ 180" +"Cartridge.Manufacturer" "Homevision" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "235" +"" + +"Cartridge.MD5" "b9c3bc1d77f8e9d814735188bf324e40" +"Cartridge.Name" "James Bond 007" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5110" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "58a82e1da64a692fd727c25faef2ecc9" +"Cartridge.Name" "Jawbreaker" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-002" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "718ae62c70af4e5fd8e932fee216948a" +"Cartridge.Name" "Journey Escape" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "112-006" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "3276c777cbe97cdd2b4a63ffc16b7151" +"Cartridge.Name" "Joust" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2691" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "39" +"Display.Height" "200" +"" + +"Cartridge.MD5" "36c29ceee2c151b23a1ad7aa04bd529d" +"Cartridge.Name" "Jr. Pac Man" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26123" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6SC" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "49" +"Display.Height" "170" +"" + +"Cartridge.MD5" "2cccc079c15e9af94246f867ffc7e9bf" +"Cartridge.Name" "Jungle Fever" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.ModelNo" "1011" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "2bb9f4686f7e08c5fcc69ec1a1c66fe7" +"Cartridge.Name" "Jungle Hunt" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2688" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F8" +"Display.FrameRate" "20" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "40" +"Display.Height" "190" +"" + +"Cartridge.MD5" "5428cdfada281c569c74c7308c7f2c26" +"Cartridge.Name" "Kaboom!" +"Cartridge.Manufacturer" "Activision (Larry Kaplan)" +"Cartridge.ModelNo" "AG-010" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "2K" +"Cartridge.Note" "Two player left paddles only" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "41" +"Display.Height" "192" +"Controller.Left" "Paddles" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "4326edb70ff20d0ee5ba58fa5cb09d60" +"Cartridge.Name" "Kangaroo" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2689" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "37" +"Display.Height" "200" +"" + +"Cartridge.MD5" "cedbd67d1ff321c996051eec843f8716" +"Cartridge.Name" "Karate" +"Cartridge.Manufacturer" "Ultravision" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "be929419902e21bd7830a7a7d746195d" +"Cartridge.Name" "Keystone Kapers" +"Cartridge.Manufacturer" "Activision (Garry Kitchen)" +"Cartridge.ModelNo" "AX-025" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "200" +"" + +"Cartridge.MD5" "7a7f6ab9215a3a6b5940b8737f116359" +"Cartridge.Name" "Killer Satellites" +"Cartridge.ModelNo" "AR-4103" +"Cartridge.Manufacturer" "Starpath (Kevin Norman)" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "36" +"Display.Height" "192" +"" + +"Cartridge.MD5" "0b1056f1091cfdc5eb0e2301f47ac6c3" +"Cartridge.Name" "King Kong" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-001" +"Cartridge.Rarity" "Rare" +"Display.YStart" "34" +"Display.Height" "210" +"" + +"Cartridge.MD5" "eed9eaf1a0b6a2b9bc4c8032cb43e3fb" +"Cartridge.Name" "Klax (PAL version)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26192" +"Cartridge.Rarity" "Extremely Rare" +"Display.Format" "PAL" +"" + +"Cartridge.MD5" "2c29182edf0965a7f56fe0897d2f84ba" +"Cartridge.Name" "Klax" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26192" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "7fcd1766de75c614a3ccc31b25dd5b7a" +"Cartridge.Name" "Knight on the Town / Lady In Wading" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "534e23210dd1993c828d944c6ac4d9fb" +"Cartridge.Name" "Kool-Aid Man" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT4648" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "195" +"" + +"Cartridge.MD5" "4baada22435320d185c95b7dd2bcdb24" +"Cartridge.Name" "Krull" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2682" +"Cartridge.Rarity" "Rare" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "5b92a93b23523ff16e2789b820e2a4c5" +"Cartridge.Name" "Kung Fu Master" +"Cartridge.Manufacturer" "Activision (Dan Kitchen)" +"Cartridge.ModelNo" "AX-039" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F8" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "42" +"Display.Height" "192" +"" + +"Cartridge.MD5" "931b91a8ea2d39fe4dca1a23832b591a" +"Cartridge.Name" "Laser Blast" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AG-008" +"Cartridge.Rarity" "Common" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "48287a9323a0ae6ab15e671ac2a87598" +"Cartridge.Name" "Laser Gates (1)" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.YStart" "34" +"Display.Height" "188" +"" + +"Cartridge.MD5" "1fa58679d4a39052bd9db059e8cda4ad" +"Cartridge.Name" "Laser Gates (2)" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.YStart" "34" +"Display.Height" "188" +"" + +"Cartridge.MD5" "4ab4af3adcdae8cdacc3d06084fc8d6a" +"Cartridge.Name" "Led Zepplin Demonstration" +"Cartridge.Manufacturer" "Nick Bensema" +"" + +"Cartridge.MD5" "71464c54da46adae9447926fdbfc1abe" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.Name" "Lock 'n' Chase" +"Cartridge.ModelNo" "MT5663" +"Cartridge.Rarity" "Common" +"Display.Height" "200" +"" + +"Cartridge.MD5" "b4e2fd27d3180f0f4eb1065afc0d7fc9" +"Cartridge.Name" "London Blitz" +"Cartridge.Manufacturer" "Avalon Hill" +"Cartridge.ModelNo" "50010" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "7c00e7a205d3fda98eb20da7c9c50a55" +"Cartridge.Name" "Lost Luggage" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2004" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "195" +"" + +"Cartridge.MD5" "393e41ca8bdd35b52bf6256a968a9b89" +"Cartridge.Name" "M.A.D." +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 1012" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.YStart" "44" +"Display.Height" "192" +"" + +"Cartridge.MD5" "ccb5fa954fb76f09caae9a8c66462190" +"Cartridge.Name" "Malagai" +"Cartridge.Manufacturer" "Answer Software" +"Cartridge.ModelNo" "ASC1001" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "54a1c1255ed45eb8f71414dadb1cf669" +"Cartridge.Name" "Mangia" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-212" +"Cartridge.Rarity" "Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"Display.Height" "200" +"" + +"Cartridge.MD5" "13895ef15610af0d0f89d588f376b3fe" +"Cartridge.Name" "Marauder" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-005" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "200" +"" + +"Cartridge.MD5" "b00e8217633e870bf39d948662a52aac" +"Cartridge.Name" "Marine Wars" +"Cartridge.Manufacturer" "Konami" +"Cartridge.ModelNo" "011" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "1b8d35d93697450ea26ebf7ff17bd4d1" +"Cartridge.Name" "Marineflieger" +"" + +"Cartridge.MD5" "e908611d99890733be31733a979c62d8" +"Cartridge.Name" "Mario Bros." +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2697" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "195" +"" + +"Cartridge.MD5" "835759ff95c2cdc2324d7c1e7c5fa237" +"Cartridge.Name" "M*A*S*H" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11011" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "37" +"Display.Height" "197" +"" + +"Cartridge.MD5" "ae4be3a36b285c1a1dff202157e2155d" +"Cartridge.Name" "Master Builder" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-210" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "3b76242691730b2dd22ec0ceab351bc6" +"Cartridge.Name" "Masters of the Universe: The Power of He-Man" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT4318" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "E7" +"Display.FrameRate" "60" +"Display.XStart" "0" +"Display.Width" "160" +"Display.YStart" "38" +"Display.Height" "185" +"TIA.PlayerDelay" "4" +"" + +"Cartridge.MD5" "470878b9917ea0348d64b5750af149aa" +"Cartridge.Name" "Math Gran Prix" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2658 / 4975128" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "8108ad2679bd055afec0a35a1dca46a4" +"Cartridge.Name" "Maze Craze / Maze Mania (1)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2635 / 4975157" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "52" +"Display.Height" "175" +"" + +"Cartridge.MD5" "f825c538481f9a7a46d1e9bc06200aaf" +"Cartridge.Name" "Maze Craze / Maze Mania (2)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2635 / 4975157" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "52" +"Display.Height" "175" +"" + +"Cartridge.MD5" "daeb54957875c50198a7e616f9cc8144" +"Cartridge.Name" "Mega Force" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11005" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "318a9d6dda791268df92d72679914ac3" +"Cartridge.Name" "Megamania" +"Cartridge.Manufacturer" "Activision (Steve Cartwright)" +"Cartridge.ModelNo" "AX-017" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "43" +"Display.Height" "192" +"" + +"Cartridge.MD5" "6522717cfd75d1dba252cbde76992090" +"Cartridge.Name" "Meteor Defense" +"Cartridge.Manufacturer" "ITT Family Games" +"Cartridge.ModelNo" "554-33391" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"" + +"Cartridge.MD5" "f1554569321dc933c87981cf5c239c43" +"Cartridge.Name" "Midnight Magic" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26129" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "37" +"Display.Height" "200" +"" + +"Cartridge.MD5" "3c57748c8286cf9e821ecd064f21aaa9" +"Cartridge.Name" "Millipede" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26118" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6SC" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "39" +"Display.Height" "192" +"" + +"Cartridge.MD5" "f0541d2f7cda5ec7bab6d62b6128b823" +"Cartridge.Name" "Bionic Breakthrough" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Cartridge.Note" "One player Mind Link controller only" +"Controller.Left" "Mindlink" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "fa0570561aa80896f0ead05c46351389" +"Cartridge.Name" "Miner 2049'er" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-008" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "3F" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "30" +"Display.Height" "216" +"" + +"Cartridge.MD5" "468f2dec984f3d4114ea84f05edf82b6" +"Cartridge.Name" "Miner 2049'er Volume II" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-011" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "3F" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "60" +"Display.Height" "212" +"" + +"Cartridge.MD5" "4543b7691914dfd69c3755a5287a95e1" +"Cartridge.Name" "Mines of Minos" +"Cartridge.Manufacturer" "CommaVid" +"Cartridge.ModelNo" "CM-005" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "35" +"Display.Height" "194" +"" + +"Cartridge.MD5" "635cc7a0db33773959d739d04eff96c2" +"Cartridge.Name" "Minesweeper" +"" + +"Cartridge.MD5" "df62a658496ac98a3aa4a6ee5719c251" +"Cartridge.Name" "Miniature Golf" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2626" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "3a2e2d0c6892aa14544083dfb7762782" +"Cartridge.Name" "Missile Command" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2638 / 4975166" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "cb24210dc86d92df97b38cf2a51782da" +"Cartridge.Name" "Missile Control" +"Cartridge.Manufacturer" "Video Gems" +"Cartridge.ModelNo" "VG-01" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "6efe876168e2d45d4719b6a61355e5fe" +"Cartridge.Name" "Mission 3000 A.D." +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG207" +"Cartridge.Rarity" "Rare" +"Display.YStart" "60" +"Display.Height" "192" +"" + +"Cartridge.MD5" "7af40c1485ce9f29b1a7b069a2eb04a7" +"Cartridge.Name" "Mogul Maniac" +"Cartridge.Manufacturer" "Amiga" +"Cartridge.ModelNo" "3120" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "190" +"" + +"Cartridge.MD5" "1f60e48ad98b659a05ce0c1a8e999ad9" +"Cartridge.Name" "Mondo Pong" +"Cartridge.Manufacturer" "Piero Cavina" +"Cartridge.Rarity" "New Release" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "6913c90002636c1487538d4004f7cac2" +"Cartridge.Name" "Monstercise" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26131" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Controller.Left" "Keypad" +"Controller.Right" "Keypad" +"" + +"Cartridge.MD5" "3347a6dd59049b15a38394aa2dafa585" +"Cartridge.Name" "Montezuma's Revenge: Starring Panama Joe" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5760" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "E0" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "515046e3061b7b18aa3a551c3ae12673" +"Cartridge.Name" "Moon Patrol" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2692" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "47" +"Display.Height" "175" +"" + +"Cartridge.MD5" "7db7c5fd8d3f53127a4bb0092c91d983" +"Cartridge.Name" "Moonsweeper (non-functional version)" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "O3207" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "203abb713c00b0884206dcc656caa48f" +"Cartridge.Name" "Moonsweeper" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "O3207" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "f5a2f6efa33a3e5541bc680e9dc31d5b" +"Cartridge.Name" "Motocross Racer" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.Rarity" "Rare" +"Display.YStart" "45" +"Display.Height" "249" +"" + +"Cartridge.MD5" "b1e2d5dc1353af6d56cd2fe7cfe75254" +"Cartridge.Name" "Motorodeo" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26171" +"Cartridge.Rarity" "Unbelievably Rare" +"" + +"Cartridge.MD5" "7e51a58de2c0db7d33715f518893b0db" +"Cartridge.Name" "Mountain King" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "4L-2738" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "FASC" +"Display.XStart" "16" +"Display.Width" "128" +"Display.YStart" "47" +"Display.Height" "186" +"" + +"Cartridge.MD5" "5678ebaa09ca3b699516dba4671643ed" +"Cartridge.Name" "Mouse Trap" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2459" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "195" +"" + +"Cartridge.MD5" "0164f26f6b38a34208cd4a2d0212afc3" +"Cartridge.Name" "Mr. Do!" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2656" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "b7a7e34e304e4b7bc565ec01ba33ea27" +"Cartridge.Name" "Mr. Do!'s Castle" +"Cartridge.Manufacturer" "Coleco / Parker Bros." +"Cartridge.ModelNo" "2695 / PB5820" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "E0" +"" + +"Cartridge.MD5" "f0daaa966199ef2b49403e9a29d12c50" +"Cartridge.Name" "Mr. Postman" +"Cartridge.Manufacturer" "Bit Corp. / CCE" +"Cartridge.ModelNo" "PG209 / C-801" +"Cartridge.Rarity" "Rare / Extremely Rare" +"" + +"Cartridge.MD5" "87e79cd41ce136fd4f72cc6e2c161bee" +"Cartridge.Name" "Ms. Pac Man" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2675" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "35" +"Display.Height" "195" +"" + +"Cartridge.MD5" "079fe9103515d15bc108577e234a484d" +"Cartridge.Name" "Multi-Color Demo" +"" + +"Cartridge.MD5" "65b106eba3e45f3dab72ea907f39f8b4" +"Cartridge.Name" "Music Machine" +"Cartridge.Manufacturer" "Sparrow" +"Cartridge.ModelNo" "GCG 1001T" +"Cartridge.Rarity" "Unbelievably Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "dfad86dd85a11c80259f3ddb6151f48f" +"Cartridge.Name" "My Golf" +"Cartridge.Manufacturer" "Absolute Entertainment / Home Entertainment Suppliers" +"Cartridge.ModelNo" "535" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "53" +"" + +"Cartridge.MD5" "392f00fd1a074a3c15bc96b0a57d52a1" +"Cartridge.Name" "Night Driver" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2633 / 4975119" +"Cartridge.Rarity" "Common" +"Cartridge.Note" "One player paddles only" +"Controller.Left" "Paddles" +"Controller.Right" "None" +"Display.Height" "200" +"" + + +"Cartridge.MD5" "f48022230bb774a7f22184b48a3385af" +"Cartridge.Name" "Night Driver (4K version)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2633 / 4975119" +"Cartridge.Rarity" "Common" +"Cartridge.Note" "One player paddles only, 4K version of 2K ROM" +"Controller.Left" "Paddles" +"Controller.Right" "None" +"Display.Height" "200" +"" + +"Cartridge.MD5" "2783006ee6519f15cbc96adae031c9a9" +"Cartridge.Name" "Night Stalker" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.YStart" "61" +"Display.Height" "199" +"" + +"Cartridge.MD5" "ed0ab909cf7b30aff6fc28c3a4660b8e" +"Cartridge.Name" "Nightmare / Stuntman" +"Cartridge.Manufacturer" "Sancho / Panda Inc." +"Cartridge.ModelNo" "TEC004 / 105" +"Cartridge.Rarity" "Extremely Rare / Rare" +"Display.Height" "205" +"" + +"Cartridge.MD5" "b6d52a0cf53ad4216feb04147301f87d" +"Cartridge.Name" "No Escape!" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3312" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.YStart" "44" +"Display.Height" "190" +"" + +"Cartridge.MD5" "de7a64108074098ba333cc0c70eef18a" +"Cartridge.Name" "Nuts" +"Cartridge.Manufacturer" "Technovision" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "194" +"" + +"Cartridge.MD5" "669840b0411bfbab5c05b786947d55d4" +"Cartridge.Name" "Obelix" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26117" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "64" +"Display.Height" "193" +"" + +"Cartridge.MD5" "4cabc895ea546022c2ecaa5129036634" +"Cartridge.Name" "Ocean City Defender" +"Cartridge.Manufacturer" "Zellers" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "36306070f0c90a72461551a7a4f3a209" +"Cartridge.Name" "Octopus" +"Cartridge.Manufacturer" "Zellers" +"Cartridge.ModelNo" "VC 1007" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "131864e1d18d3406048700d3c0760417" +"Cartridge.Name" "Off The Wall (1)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26168" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "98f63949e656ff309cefa672146dc1b8" +"Cartridge.Name" "Off The Wall (2)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26168" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "b6166f15720fdf192932f1f76df5b65d" +"Cartridge.Name" "Off Your Rocker" +"Cartridge.Manufacturer" "Amiga" +"Cartridge.ModelNo" "3130" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "c9c25fc536de9a7cdc5b9a916c459110" +"Cartridge.Name" "Oink!" +"Cartridge.Manufacturer" "Activision (Mike Lorenzen)" +"Cartridge.ModelNo" "AX-023" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "42" +"Display.Height" "194" +"" + +"Cartridge.MD5" "ce4bbe11d682c15a490ae15a4a8716cf" +"Cartridge.Name" "Okie Dokie" +"Cartridge.Manufacturer" "RetroWare" +"Cartridge.Rarity" "Uncommon / New Release" +"Cartridge.Type" "2K" +"Display.XStart" "16" +"Display.Width" "128" +"Display.YStart" "30" +"Display.Height" "178" +"" + +"Cartridge.MD5" "9947f1ebabb56fd075a96c6d37351efa" +"Cartridge.Name" "Omega Race" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "4L-2737" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Right difficulty 'A' to use Booster-Grip in both ports" +"Console.RightDifficulty" "A" +"Controller.Left" "Booster-Grip" +"Controller.Right" "Booster-Grip" +"" + +"Cartridge.MD5" "28d5df3ed036ed63d33a31d0d8b85c47" +"Cartridge.Name" "Open Sesame" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG204" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "fa1b060fd8e0bca0c2a097dcffce93d3" +"Cartridge.Name" "Oscar's Trash Race" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26101" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "One player keypad only" +"Controller.Left" "Keypad" +"Controller.Right" "None" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "55949cb7884f9db0f8dfcf8707c7e5cb" +"Cartridge.Name" "Othello" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2639 / 4975162" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "890c13590e0d8d5d6149737d930e4d95" +"Cartridge.Name" "Outlaw" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2605" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "91f0a708eeb93c133e9672ad2c8e0429" +"Cartridge.Name" "Oystron" +"Cartridge.Manufacturer" "Piero Cavina" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "936ef1d6f8a57b9ff575dc195ee36b80" +"Cartridge.Name" "Pac-Kong" +"Cartridge.Manufacturer" "Funvision" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "6e372f076fb9586aff416144f5cfe1cb" +"Cartridge.Name" "Pac-Man" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2646 / 4975185" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "36" +"Display.Height" "202" +"" + +"Cartridge.MD5" "f8582bc6ca7046adb8e18164e8cecdbc" +"Cartridge.Name" "Panda Chase" +"Cartridge.Manufacturer" "Homevision" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "249" +"Timer.Adjustment" "1" +"" + +"Cartridge.MD5" "714e13c08508ee9a7785ceac908ae831" +"Cartridge.Name" "Parachute" +"Cartridge.Manufacturer" "Homevision" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "47" +"Display.Height" "220" +"" + +"Cartridge.MD5" "012b8e6ef3b5fd5aabc94075c527709d" +"Cartridge.Name" "Party Mix" +"Cartridge.ModelNo" "AR-4302" +"Cartridge.Manufacturer" "Starpath (Dennis Caswell)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"Emulation.CPU" "High" +"Display.XStart" "0" +"Display.Width" "160" +"Display.YStart" "34" +"Display.Height" "192" +"" + +"Cartridge.MD5" "4c0fb2544ae0f8b5f7ae8bce7bd7f134" +"Cartridge.Name" "Party Mix - Demonstration" +"Cartridge.ModelNo" "AR-4302" +"Cartridge.Manufacturer" "Starpath (Dennis Caswell)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "0" +"Display.Width" "160" +"Display.YStart" "34" +"Display.Height" "192" +"" + +"Cartridge.MD5" "e40a818dac4dd851f3b4aafbe2f1e0c1" +"Cartridge.Name" "Peek-A-Boo" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26137" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"" + +"Cartridge.MD5" "ace319dc4f76548659876741a6690d57" +"Cartridge.Name" "Pele's Soccer / Pele's Championship Soccer" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2616" +"Cartridge.Rarity" "Common" +"Display.Height" "192" +"" + +"Cartridge.MD5" "7a09299f473105ae1ef3ad6f9f2cd807" +"Cartridge.Name" "Pele's Soccer / Pele's Championship Soccer (Pirate version)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2616" +"Cartridge.Rarity" "Common" +"Display.Height" "192" +"" + +"Cartridge.MD5" "4bcc7f6ba501a26ee785b7efbfb0fdc8" +"Cartridge.Name" "Pengo" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2690" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "F8" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "35" +"Display.Height" "196" +"" + +"Cartridge.MD5" "09388bf390cd9a86dc0849697b96c7dc" +"Cartridge.Name" "Pete Rose Baseball" +"Cartridge.Manufacturer" "Absolute Entertainment" +"Cartridge.ModelNo" "AK-045" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "38" +"Display.Height" "190" +"" + +"Cartridge.MD5" "6b1fc959e28bd71aed7b89014574bdc2" +"Cartridge.Name" "Phantom Tank" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG203" +"Cartridge.Rarity" "Rare" +"Display.Height" "219" +"Display.YStart" "47" +"Display.YStart" "42" +"Display.Height" "245" +"" + +"Cartridge.MD5" "62f74a2736841191135514422b20382d" +"Cartridge.Name" "Pharoah's Curse" +"Cartridge.Manufacturer" "Technovision" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "4K" +"Display.YStart" "10" +"Display.Height" "200" +"Display.YStart" "22" +"Display.Height" "225" +"" + +"Cartridge.MD5" "7dcbfd2acc013e817f011309c7504daa" +"Cartridge.Name" "Phasor Patrol" +"Cartridge.ModelNo" "AR-4000" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "34" +"Display.Height" "192" +"" + +"Cartridge.MD5" "ca54de69f7cdf4d7996e86f347129892" +"Cartridge.Name" "Philly Flasher" +"Cartridge.Manufacturer" "Mystique" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "7e52a95074a66640fcfde124fffd491a" +"Cartridge.Name" "Phoenix" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2673" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Display.YStart" "38" +"Display.Height" "188" +"" + +"Cartridge.MD5" "da79aad11572c80a96e261e4ac6392d0" +"Cartridge.Name" "Pick 'N' Pile" +"Cartridge.Manufacturer" "Salu" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "52" +"Display.Height" "219" +"" + +"Cartridge.MD5" "17c0a63f9a680e7a61beba81692d9297" +"Cartridge.Name" "Picnic" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2004" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "192" +"" + +"Cartridge.MD5" "d3423d7600879174c038f53e5ebbf9d3" +"Cartridge.Name" "Piece 'O Cake" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2005" +"Cartridge.Rarity" "Extremely Rare" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "8e4fa8c6ad8d8dce0db8c991c166cdaa" +"Cartridge.Name" "Pigs In Space" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26114" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "3e90cf23106f2e08b2781e41299de556" +"Cartridge.Name" "Pitfall!" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AX-018" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "41" +"Display.Height" "192" +"" + +"Cartridge.MD5" "f73d2d0eff548e8fc66996f27acf2b4b" +"Cartridge.Name" "Pitfall! (CCE Version)" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AX-018" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "41" +"Display.Height" "192" +"" + +"Cartridge.MD5" "f939780714db69dc69a80fbefe350e0d" +"Cartridge.Name" "Pitfall II: Lost Caverns" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AB-035-04" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "48eb1fcde4caf6a2dce059c98bd2e375" +"Cartridge.Name" "Pitfall II: Lost Caverns (10K version) (1)" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AB-035-04" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "6d842c96d5a01967be9680080dd5be54" +"Cartridge.Name" "Pitfall II: Lost Caverns (10K version) (2)" +"Cartridge.Manufacturer" "Activision (David Crane)" +"Cartridge.ModelNo" "AB-035-04" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "043f165f384fbea3ea89393597951512" +"Cartridge.Name" "Planet Patrol" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-202" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "da4e3396aa2db3bd667f83a1cb9e4a36" +"Cartridge.Name" "Plaque Attack" +"Cartridge.Manufacturer" "Activision (Steve Cartwright)" +"Cartridge.ModelNo" "AX-027" +"Cartridge.Rarity" "Rare" +"Display.YStart" "47" +"Display.Height" "190" +"" + +"Cartridge.MD5" "5f39353f7c6925779b0169a87ff86f1e" +"Cartridge.Name" "Pole Position" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2694" +"Cartridge.Rarity" "Common" +"Display.YStart" "42" +"Display.Height" "186" +"" + +"Cartridge.MD5" "203049f4d8290bb4521cc4402415e737" +"Cartridge.Name" "Polaris" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-007" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "3F" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "62" +"Display.Height" "210" +"" + +"Cartridge.MD5" "ee28424af389a7f3672182009472500c" +"Cartridge.Name" "Polo" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"" + +"Cartridge.MD5" "4799a40b6e889370b7ee55c17ba65141" +"Cartridge.Name" "Pooyan" +"Cartridge.Manufacturer" "Gakken" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "c7f13ef38f61ee2367ada94fdcc6d206" +"Cartridge.Name" "Popeye" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5370" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "f93d7fee92717e161e6763a88a293ffa" +"Cartridge.Name" "Porky's" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11013" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"" + +"Cartridge.MD5" "97d079315c09796ff6d95a06e4b70171" +"Cartridge.Name" "Pressure Cooker" +"Cartridge.Manufacturer" "Activision (Garry Kitchen)" +"Cartridge.ModelNo" "AZ-032" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "195" +"" + +"Cartridge.MD5" "ef3a4f64b6494ba770862768caf04b86" +"Cartridge.Name" "Private Eye" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AG-034-04" +"Cartridge.Rarity" "Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "12123b534bdee79ed7563b9ad74f1cbd" +"Cartridge.Name" "Pro Wrestling" +"Cartridge.Manufacturer" "Activision / Absolute Entertainment" +"Cartridge.ModelNo" "AG-041" +"Cartridge.Rarity" "Extremely Rare / Rare" +"Display.YStart" "47" +"Display.Height" "184" +"" + +"Cartridge.MD5" "37fd7fa52d358f66984948999f1213c5" +"Cartridge.Name" "Pyramid War" +"Cartridge.Manufacturer" "Suntek" +"Cartridge.ModelNo" "4" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "67" +"Display.Height" "184" +"" + +"Cartridge.MD5" "484b0076816a104875e00467d431c2d2" +"Cartridge.Name" "Q*Bert" +"Cartridge.Manufacturer" "Parker Bros. / Atari" +"Cartridge.ModelNo" "PB5360 / CX26150" +"Cartridge.Rarity" "Common / Rare" +"Cartridge.Type" "4K" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "45" +"Display.Height" "202" +"" + +"Cartridge.MD5" "72b8dc752befbfb3ffda120eb98b2dd0" +"Cartridge.Name" "Q*Bert's Qubes (1)" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5550" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "E0" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "32" +"Display.Height" "210" +"" + +"Cartridge.MD5" "517592e6e0c71731019c0cebc2ce044f" +"Cartridge.Name" "Q*Bert's Qubes (2)" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5550" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "E0" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "32" +"Display.Height" "210" +"" + +"Cartridge.MD5" "392d34c0498075dd58df0ce7cd491ea2" +"Cartridge.Name" "Quadrun" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2686" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "a0675883f9b09a3595ddd66a6f5d3498" +"Cartridge.Name" "Quest for Quinta Roo" +"Cartridge.Manufacturer" "Telegames (Sunrise)" +"Cartridge.ModelNo" "6057 A227" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "192" +"" + +"Cartridge.MD5" "7eba20c2291a982214cc7cbe8d0b47cd" +"Cartridge.Name" "Quick Step" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "O3211" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "195" +"" + +"Cartridge.MD5" "fb4ca865abc02d66e39651bd9ade140a" +"Cartridge.Name" "Rabbit Transit" +"Cartridge.ModelNo" "AR-4104" +"Cartridge.Manufacturer" "Starpath (Brian McGhie)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "34" +"Display.Height" "198" +"" + +"Cartridge.MD5" "cd399bc422992a361ba932cc50f48b65" +"Cartridge.Name" "Rabbit Transit - Demonstration" +"Cartridge.ModelNo" "AR-4104" +"Cartridge.Manufacturer" "Starpath (Brian McGhie)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "34" +"Display.Height" "198" +"" + +"Cartridge.MD5" "a20d931a8fddcd6f6116ed21ff5c4832" +"Cartridge.Name" "Racquetball" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2003" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "190" +"" + +"Cartridge.MD5" "baf4ce885aa281fd31711da9b9795485" +"Cartridge.Name" "Radar Lock" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26176" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6SC" +"Display.YStart" "42" +"Display.Height" "185" +"" + +"Cartridge.MD5" "92a1a605b7ad56d863a56373a866761b" +"Cartridge.Name" "Raft Rider" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2006" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "f724d3dd2471ed4cf5f191dbb724b69f" +"Cartridge.Name" "Raiders of the Lost Ark" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2659" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Display.YStart" "37" +"Display.Height" "192" +"" + +"Cartridge.MD5" "7096a198531d3f16a99d518ac0d7519a" +"Cartridge.Name" "Ram It" +"Cartridge.Manufacturer" "Telesys" +"Cartridge.ModelNo" "1004" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "5e1b4629426f4992cf3b2905a696e1a7" +"Cartridge.Name" "Rampage!" +"Cartridge.Manufacturer" "Activision (Bobco)" +"Cartridge.ModelNo" "AK-049" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.XStart" "12" +"Display.Width" "136" +"Display.YStart" "38" +"Display.Height" "200" +"" + +"Cartridge.MD5" "9f8fad4badcd7be61bbd2bcaeef3c58f" +"Cartridge.Name" "Reactor" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5330" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "eb634650c3912132092b7aee540bbce3" +"Cartridge.Name" "RealSports Baseball" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2640" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "32" +"Display.Height" "200" +"" + +"Cartridge.MD5" "3177cc5c04c1a4080a927dfa4099482b" +"Cartridge.Name" "RealSports Boxing" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26135" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6" +"Display.Height" "185" +"" + +"Cartridge.MD5" "7ad257833190bc60277c1ca475057051" +"Cartridge.Name" "RealSports Football" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2668" +"Cartridge.Rarity" "Common" +"Display.Height" "200" +"" + +"Cartridge.MD5" "08f853e8e01e711919e734d85349220d" +"Cartridge.Name" "RealSports Soccer" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2667" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "c7eab66576696e11e3c11ffff92e13cc" +"Cartridge.Name" "RealSports Tennis (1)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2680" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "57" +"" + +"Cartridge.MD5" "dac5c0fe74531f077c105b396874a9f1" +"Cartridge.Name" "RealSports Tennis (2)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2680" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "57" +"" + +"Cartridge.MD5" "aed0b7bd64cc384f85fdea33e28daf3b" +"Cartridge.Name" "RealSports Volleyball" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2666" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "41" +"Display.Height" "192" +"" + +"Cartridge.MD5" "8a9d874a38608964f33ec0c35cab618d" +"Cartridge.Name" "Rescue Bira Bira" +"Cartridge.Manufacturer" "Chris Cracknell / Mystique" +"Cartridge.Note" "Less adult-oriented version of Mystique's Jungle Fever" +"" + +"Cartridge.MD5" "60a61da9b2f43dd7e13a5093ec41a53d" +"Cartridge.Name" "Rescue Terra I" +"Cartridge.Manufacturer" "Venture Vision" +"Cartridge.ModelNo" "VV2001" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "4f64d6d0694d9b7a1ed7b0cb0b83e759" +"Cartridge.Name" "Revenge of the Beefsteak Tomatoes" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11016" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "a995b6cbdb1f0433abc74050808590e6" +"Cartridge.Name" "Riddle of the Sphinx" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3600" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "40" +"Display.Height" "185" +"" + +"Cartridge.MD5" "31512cdfadfd82bfb6f196e3b0fd83cd" +"Cartridge.Name" "River Patrol" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-004" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "3F" +"Display.YStart" "40" +"Display.Height" "192" +"" + +"Cartridge.MD5" "393948436d1f4cc3192410bb918f9724" +"Cartridge.Name" "River Raid (1)" +"Cartridge.Manufacturer" "Activision (Carol Shaw)" +"Cartridge.ModelNo" "AX-020" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.YStart" "36" +"Display.Height" "199" +"" + +"Cartridge.MD5" "291cc37604bc899e8e065c30153fc4b9" +"Cartridge.Name" "River Raid (2)" +"Cartridge.Manufacturer" "Activision (Carol Shaw)" +"Cartridge.ModelNo" "AX-020" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.YStart" "36" +"Display.Height" "199" +"" + +"Cartridge.MD5" "6ce2110ac5dd89ab398d9452891752ab" +"Cartridge.Name" "River Raid (Polyvox version)" +"Cartridge.Manufacturer" "Polyvox" +"Cartridge.Type" "4K" +"Display.YStart" "36" +"Display.Height" "199" +"" + +"Cartridge.MD5" "dd92d6ad50976f881d86b52d38616118" +"Cartridge.Name" "River Raid (SpkSoft version)" +"Cartridge.Manufacturer" "SpkSoft" +"Cartridge.Note" "Variant of River Raid by Activision (Carol Shaw)" +"" + +"Cartridge.MD5" "ab56f1b2542a05bebc4fbccfc4803a38" +"Cartridge.Name" "River Raid II" +"Cartridge.Manufacturer" "Activision (Dan Kitchen / Imagineering)" +"Cartridge.ModelNo" "AK-048-04" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "42" +"Display.Height" "195" +"" + +"Cartridge.MD5" "2bd00beefdb424fa39931a75e890695d" +"Cartridge.Name" "Road Runner" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2663" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "72a46e0c21f825518b7261c267ab886e" +"Cartridge.Name" "Robin Hood" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99005" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "25" +"Display.Height" "225" +"" + +"Cartridge.MD5" "1bef389e3dd2d4ca4f2f60d42c932509" +"Cartridge.Name" "Robot Fight" +"Cartridge.Manufacturer" "Homevision" +"Cartridge.ModelNo" "1" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "4f618c2429138e0280969193ed6c107e" +"Cartridge.Name" "Robot Tank" +"Cartridge.Manufacturer" "Activision (Alan Miller)" +"Cartridge.ModelNo" "AZ-028" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "FE" +"" + +"Cartridge.MD5" "67931b0d37dc99af250dd06f1c095e8d" +"Cartridge.Name" "Room of Doom" +"Cartridge.Manufacturer" "CommaVid" +"Cartridge.ModelNo" "CM-004" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "65bd29e8ab1b847309775b0de6b2e4fe" +"Cartridge.Name" "Roc 'n' Rope" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2667" +"Cartridge.Rarity" "Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "1ec57bbd27bdbd08b60c391c4895c1cf" +"Cartridge.Name" "Saboteur" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26119" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.YStart" "44" +"Display.Height" "190" +"" + +"Cartridge.MD5" "ed1a784875538c7871d035b7a98c2433" +"Cartridge.Name" "Save Our Ship" +"Cartridge.Manufacturer" "Technovision" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "19e761e53e5ec8e9f2fceea62715ca06" +"Cartridge.Name" "Scuba Diver" +"Cartridge.Manufacturer" "Panda Inc." +"Cartridge.ModelNo" "104" +"Cartridge.Rarity" "Rare" +"Display.YStart" "44" +"Display.Height" "197" +"" + +"Cartridge.MD5" "07f42847a79e4f5ae55cc03304b18c25" +"Cartridge.Name" "Sea Hawk" +"Cartridge.Manufacturer" "Froggo / Panda Inc. / Sancho" +"Cartridge.ModelNo" "FG 1008 / 108 / TEC002" +"Cartridge.Rarity" "Rare / Rare / Extremely Rare" +"Display.YStart" "37" +"Display.Height" "202" +"" + +"Cartridge.MD5" "624e0a77f9ec67d628211aaf24d8aea6" +"Cartridge.Name" "Sea Hawk (Pirate version)" +"Cartridge.Manufacturer" "Froggo / Panda Inc. / Sancho" +"Cartridge.ModelNo" "FG 1008 / 108 / TEC002" +"Cartridge.Rarity" "Rare / Rare / Extremely Rare" +"Display.YStart" "37" +"Display.Height" "202" +"" + +"Cartridge.MD5" "5dccf215fdb9bbf5d4a6d0139e5e8bcb" +"Cartridge.Name" "Sea Hunt" +"Cartridge.Manufacturer" "Froggo" +"Cartridge.ModelNo" "FG 1009" +"Cartridge.Rarity" "Rare" +"Display.YStart" "44" +"Display.Height" "197" +"" + + +"Cartridge.MD5" "68489e60268a5e6e052bad9c62681635" +"Cartridge.Name" "Sea Monster" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG201" +"Cartridge.Rarity" "Rare" +"Display.YStart" "37" +"Display.Height" "259" +"" + +"Cartridge.MD5" "240bfbac5163af4df5ae713985386f92" +"Cartridge.Name" "Seaquest" +"Cartridge.Manufacturer" "Activision (Steve Cartwright)" +"Cartridge.ModelNo" "AX-022" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "195" +"" + +"Cartridge.MD5" "fc24a94d4371c69bc58f5245ada43c44" +"Cartridge.Name" "Secret Quest" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26170" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6SC" +"Display.XStart" "16" +"Display.Width" "128" +"Display.YStart" "42" +"Display.Height" "194" +"" + +"Cartridge.MD5" "efffafc17b7cb01b9ca35324aa767364" +"Cartridge.Name" "Seesaw" +"Cartridge.Manufacturer" "Cooper Black" +"Cartridge.Note" "Variant of Circus Atari (Joystick Version) by Atari" +"Cartridge.ModelNo" "CX2630" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "42" +"Display.Height" "190" +"" + +"Cartridge.MD5" "8da51e0c4b6b46f7619425119c7d018e" +"Cartridge.Name" "Sentinel" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26183" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "One player light gun only" +"Controller.Left" "Lightgun" +"Controller.Right" "None" +"Cartridge.Type" "F6" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "46" +"Display.Height" "192" +"" + +"Cartridge.MD5" "54f7efa6428f14b9f610ad0ca757e26c" +"Cartridge.Name" "Shark Attack" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2005" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "22" +"Display.Height" "219" +"" + +"Cartridge.MD5" "b5a1a189601a785bdb2f02a424080412" +"Cartridge.Name" "Shootin' Gallery" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3410" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "25b6dc012cdba63704ea9535c6987beb" +"Cartridge.Name" "Shuttle Orbiter" +"Cartridge.Manufacturer" "Avalon Hill" +"Cartridge.ModelNo" "50040" +"Cartridge.Rarity" "Unbelievably Rare" +"" + +"Cartridge.MD5" "ea38fcfc06ad87a0aed1a3d1588744e4" +"Cartridge.Name" "Sinistar" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26122" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.YStart" "37" +"Display.Height" "219" +"" + +"Cartridge.MD5" "7ead257e8b5a44cac538f5f54c7a0023" +"Cartridge.Name" "Sir Lancelot" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "7ead257e8b5a44cac538f5f54c7a0023" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "f847fb8dba6c6d66d13724dbe5d95c4d" +"Cartridge.Name" "Skateboardin'" +"Cartridge.Manufacturer" "Activision (David Crane) / Absolute Entertainment" +"Cartridge.ModelNo" "AZ-042 / AG-042" +"Cartridge.Rarity" "Extremely Rare / Rare" +"Display.YStart" "34" +"Display.Height" "210" +"" + +"Cartridge.MD5" "39c78d682516d79130b379fa9deb8d1c" +"Cartridge.Name" "Skeet Shoot" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 1001" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "8654d7f0fb351960016e06646f639b02" +"Cartridge.Name" "Ski Hunt" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "37" +"Display.Height" "235" +"Timer.Adjustment" "2" +"" + +"Cartridge.MD5" "f10e3f45fb01416c87e5835ab270b53a" +"Cartridge.Name" "Ski Run" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.ModelNo" "TP-607" +"Display.YStart" "52" +"Display.Height" "205" +"" + +"Cartridge.MD5" "b76fbadc8ffb1f83e2ca08b6fb4d6c9f" +"Cartridge.Name" "Skiing" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AG-005" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "200" +"" + +"Cartridge.MD5" "46c021a3e9e2fd00919ca3dd1a6b76d8" +"Cartridge.Name" "Sky Diver" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2629" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "8bd8f65377023bdb7c5fcf46ddda5d31" +"Cartridge.Name" "Sky Jinks (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AG-019" +"Cartridge.Rarity" "Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "2a0ba55e56e7a596146fa729acf0e109" +"Cartridge.Name" "Sky Jinks" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AG-019" +"Cartridge.Rarity" "Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "3b91c347d8e6427edbe942a7a405290d" +"Cartridge.Name" "Sky Skipper" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5350" +"Cartridge.Rarity" "Rare" +"Display.Height" "200" +"" + +"Cartridge.MD5" "f90b5da189f24d7e1a2117d8c8abc952" +"Cartridge.Name" "Slot Machine" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2653" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "aed82052f7589df05a3f417bb4e45f0c" +"Cartridge.Name" "Slot Racers" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2606" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "34" +"" + +"Cartridge.MD5" "24aff972d58990f9b88a6d787c796f1e" +"Cartridge.Name" "Smurf Rescue in Gargamel's Castle (PAL version)" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2465" +"Cartridge.Rarity" "Uncommon" +"Display.Format" "PAL" +"" + +"Cartridge.MD5" "3d1e83afdb4265fa2fb84819c9cfd39c" +"Cartridge.Name" "Smurf Rescue in Gargamel's Castle" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2465" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "a204cd4fb1944c86e800120706512a64" +"Cartridge.Name" "Smurfs Save The Day" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2511" +"Cartridge.Rarity" "Unbelievably Rare" +"" + +"Cartridge.MD5" "898b5467551d32af48a604802407b6e8" +"Cartridge.Name" "Snail Against Squirrel" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG208" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "245" +"" + +"Cartridge.MD5" "9c6faa4ff7f2ae549bbcb14f582b70e4" +"Cartridge.Name" "Sneak 'N' Peak" +"Cartridge.Manufacturer" "U.S. Games (Vidtec)" +"Cartridge.ModelNo" "VC 1002" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"Display.Height" "229" +"" + +"Cartridge.MD5" "57939b326df86b74ca6404f64f89fce9" +"Cartridge.Name" "Snoopy and the Red Baron" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26111" +"Cartridge.Rarity" "Rare" +"Display.Height" "192" +"" + +"Cartridge.MD5" "947317a89af38a49c4864d6bdd6a91fb" +"Cartridge.Name" "Solar Fox" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "4L-2487" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "97842fe847e8eb71263d6f92f7e122bd" +"Cartridge.Name" "Solar Storm" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "O3206" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "42" +"Display.Height" "190" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "e72eb8d4410152bdcb69e7fba327b420" +"Cartridge.Name" "Solaris" +"Cartridge.Manufacturer" "Atari (Douglas Neubauer)" +"Cartridge.ModelNo" "CX26136" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6" +"Display.YStart" "38" +"Display.Height" "193" +"" + +"Cartridge.MD5" "d2c4f8a4a98a905a9deef3ba7380ed64" +"Cartridge.Name" "Sorcerer" +"Cartridge.Manufacturer" "Mythicon" +"Cartridge.ModelNo" "MA-1001" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "5f7ae9a7f8d79a3b37e8fc841f65643a" +"Cartridge.Name" "Sorcerer's Apprentice" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26109" +"Cartridge.Rarity" "Rare" +"Display.Height" "192" +"" + +"Cartridge.MD5" "17badbb3f54d1fc01ee68726882f26a6" +"Cartridge.Name" "Space Attack" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5659" +"Cartridge.Rarity" "Common" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "559317712f989f097ea464517f1a8318" +"Cartridge.Name" "Space Canyon" +"Cartridge.Manufacturer" "Panda Inc." +"Cartridge.ModelNo" "100" +"Cartridge.Rarity" "Rare" +"Display.YStart" "35" +"Display.Height" "184" +"" + +"Cartridge.MD5" "df6a28a89600affe36d94394ef597214" +"Cartridge.Name" "Space Cavern" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2002" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "72ffbef6504b75e69ee1045af9075f66" +"Cartridge.Name" "Space Invaders" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2632 / 4975153" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "012020625a3227815e47b37fd025e480" +"Cartridge.Name" "Space Invaders (alternate version)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2632" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "6f2aaffaaf53d23a28bf6677b86ac0e3" +"Cartridge.Name" "Space Jockey" +"Cartridge.Manufacturer" "U.S. Games (Vidtec)" +"Cartridge.ModelNo" "VC 1001" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "45040679d72b101189c298a864a5b5ba" +"Cartridge.Name" "SpaceMaster X-7" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11022" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "200" +"" + +"Cartridge.MD5" "1a624e236526c4c8f31175e9c89b2a22" +"Cartridge.Name" "Space Raid" +"Cartridge.Manufacturer" "Rainbow Vision" +"Cartridge.ModelNo" "SS-007" +"Display.Height" "200" +"" + +"Cartridge.MD5" "3dfb7c1803f937fadc652a3e95ff7dc6" +"Cartridge.Name" "Space Robot" +"Cartridge.Manufacturer" "Artic" +"Cartridge.ModelNo" "SM8001" +"Cartridge.Rarity" "Unbelievably Rare" +"" + +"Cartridge.MD5" "5894c9c0c1e7e29f3ab86c6d3f673361" +"Cartridge.Name" "Space Shuttle" +"Cartridge.Manufacturer" "Activision (Steve Kitchen)" +"Cartridge.ModelNo" "AZ-033" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "df2745d585238780101df812d00b49f4" +"Cartridge.Name" "Space Tunnel (1)" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG202" +"Cartridge.Rarity" "Rare" +"Display.YStart" "37" +"Display.Height" "259" +"" + +"Cartridge.MD5" "8917f7c1ac5eb05b82331cf01c495af2" +"Cartridge.Name" "Space Tunnel (2)" +"Cartridge.Manufacturer" "Bit Corp." +"Cartridge.ModelNo" "PG202" +"Cartridge.Rarity" "Rare" +"Display.YStart" "37" +"Display.Height" "259" +"" + +"Cartridge.MD5" "7e9da5cb84d5bc869854938fe3e85ffa" +"Cartridge.Name" "Space War (4K version)" +"Cartridge.Note" "4K version of 2K ROM" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2604" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "200" +"" + +"Cartridge.MD5" "a7ef44ccb5b9000caf02df3e6da71a92" +"Cartridge.Name" "Space War" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2604" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "200" +"" + +"Cartridge.MD5" "ec5c861b487a5075876ab01155e74c6c" +"Cartridge.Name" "Spacechase" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2001" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "24d018c4a6de7e5bd19a36f2b879b335" +"Cartridge.Name" "Spider Fighter" +"Cartridge.Manufacturer" "Activision (Larry Miller)" +"Cartridge.ModelNo" "AX-021" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.YStart" "35" +"Display.Height" "202" +"" + +"Cartridge.MD5" "199eb0b8dce1408f3f7d46411b715ca9" +"Cartridge.Name" "Spider-Man" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5900" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "33" +"Display.Height" "200" +"" + +"Cartridge.MD5" "8454ed9787c9d8211748ccddb673e920" +"Cartridge.Name" "Spiderdroid" +"Cartridge.Manufacturer" "Froggo" +"Cartridge.Rarity" "Rare" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "40" +"Display.Height" "182" +"" + +"Cartridge.MD5" "a4e885726af9d97b12bb5a36792eab63" +"Cartridge.Name" "Spike's Peak" +"Cartridge.Manufacturer" "Xonox" +"Cartridge.ModelNo" "99001" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "cef2287d5fd80216b2200fb2ef1adfa8" +"Cartridge.Name" "Spitfire Attack" +"Cartridge.Manufacturer" "Milton Bradley" +"Cartridge.ModelNo" "4363" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "4cd796b5911ed3f1062e805a3df33d98" +"Cartridge.Name" "Springer" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-006" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "3F" +"Display.YStart" "36" +"Display.Height" "202" +"" + +"Cartridge.MD5" "5a8afe5422abbfb0a342fb15afd7415f" +"Cartridge.Name" "Sprintmaster" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26155" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6SC" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "27" +"Display.Height" "192" +"" + +"Cartridge.MD5" "3105967f7222cc36a5ac6e5f6e89a0b4" +"Cartridge.Name" "Spy Hunter" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "011-02" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "F8" +"Display.YStart" "32" +"Display.Height" "200" +"TIA.PlayerDelay" "0" +"" + +"Cartridge.MD5" "ba257438f8a78862a9e014d831143690" +"Cartridge.Name" "Squeeze Box" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 2002" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "21a96301bb0df27fde2e7eefa49e0397" +"Cartridge.Name" "Sssnake" +"Cartridge.Manufacturer" "Data Age / Gameworld" +"Cartridge.ModelNo" "DA 1003 / DA 1002" +"Cartridge.Rarity" "Common / Extremely Rare" +"Display.Height" "219" +"" + +"Cartridge.MD5" "21d7334e406c2407e69dbddd7cec3583" +"Cartridge.Name" "Stampede" +"Cartridge.Manufacturer" "Activision (Bob Whitehead)" +"Cartridge.ModelNo" "AG-011" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "2K" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "38bd172da8b2a3a176e517c213fcd5a6" +"Cartridge.Name" "Standalone Test Tape" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "MAO17600" +"" + +"Cartridge.MD5" "f526d0c519f5001adb1fc7948bfbb3ce" +"Cartridge.Name" "Star Fox" +"Cartridge.Manufacturer" "Mythicon" +"Cartridge.ModelNo" "MA-1003" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "54" +"Display.Height" "192" +"" + +"Cartridge.MD5" "57fa2d09c9e361de7bd2aa3a9575a760" +"Cartridge.Name" "Stargate (1)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26120" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "Variant of Defender II by Atari" +"Cartridge.Type" "F8SC" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "37" +"Display.Height" "190" +"" + +"Cartridge.MD5" "0c48e820301251fbb6bcdc89bd3555d9" +"Cartridge.Name" "Stargate (2)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26120" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "Variant of Defender II by Atari" +"Cartridge.Type" "F8SC" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "37" +"Display.Height" "190" +"" + +"Cartridge.MD5" "a3c1c70024d7aabb41381adbfb6d3b25" +"Cartridge.Name" "Star Gunner" +"Cartridge.Manufacturer" "Telesys" +"Cartridge.ModelNo" "1005" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "cbd981a23c592fb9ab979223bb368cd5" +"Cartridge.Name" "Star Raiders" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2660 / 4975187" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Uses Joystick/Keypad combination" +"Controller.Right" "Keypad" +"" + +"Cartridge.MD5" "e363e467f605537f3777ad33e74e113a" +"Cartridge.Name" "Star Ship / Outer Space" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2603 / 6699803 / 4975601" +"Cartridge.Rarity" "Rare" +"Display.YStart" "32" +"Display.Height" "219" +"" + +"Cartridge.MD5" "79e5338dbfa6b64008bb0d72a3179d3c" +"Cartridge.Name" "Star Strike" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT4313" +"Cartridge.Rarity" "Rare" +"Display.YStart" "43" +"Display.Height" "193" +"" + +"Cartridge.MD5" "03c3f7ba4585e349dd12bfa7b34b7729" +"Cartridge.Name" "Star Trek: Strategic Operations Simulator" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "004-01" +"Cartridge.Rarity" "Rare" +"Display.YStart" "35" +"Display.Height" "219" +"" + +"Cartridge.MD5" "813985a940aa739cc28df19e0edd4722" +"Cartridge.Name" "Star Voyager" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3201" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "6339d28c9a7f92054e70029eb0375837" +"Cartridge.Name" "Star Wars: The Arcade Game" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5540" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "E0" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "39" +"Display.Height" "184" +"" + +"Cartridge.MD5" "5336f86f6b982cc925532f2e80aa1e17" +"Cartridge.Name" "Star Wars: Return of the Jedi, Death Star Battle" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5060" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "E0" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "3c8e57a246742fa5d59e517134c0b4e6" +"Cartridge.Name" "Star Wars: The Empire Strikes Back" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5050" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "41" +"Display.Height" "188" +"" + +"Cartridge.MD5" "c9f6e521a49a2d15dac56b6ddb3fb4c7" +"Cartridge.Name" "Star Wars: Jedi Arena" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5000" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "4K" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "31" +"Display.Height" "199" +"Cartridge.Note" "Uses swapped paddles" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "d69559f9c9dc6ef528d841bf9d91b275" +"Cartridge.Name" "Starmaster" +"Cartridge.Manufacturer" "Activision (Alan Miller)" +"Cartridge.ModelNo" "AX-016" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "656dc247db2871766dffd978c71da80c" +"Cartridge.Name" "Steeplechase" +"Cartridge.Manufacturer" "Atari (available with Sears label only)" +"Cartridge.ModelNo" "CX2614 / 4975126" +"Cartridge.Rarity" "Rare" +"Display.Height" "198" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "47aef18509051bab493589cb2619170b" +"Cartridge.Name" "Stell-A-Sketch" +"Cartridge.Manufacturer" "Retroware" +"Cartridge.Rarity" "New Release" +"Cartridge.Note" "Uses two steering controllers" +"Controller.Left" "Driving" +"Controller.Right" "Driving" +"" + +"Cartridge.MD5" "0b8d3002d8f744a753ba434a4d39249a" +"Cartridge.Name" "Stellar Track" +"Cartridge.Manufacturer" "Atari (available with Sears label only)" +"Cartridge.ModelNo" "CX2619 / 4975159" +"Cartridge.Rarity" "Rare" +"Display.XStart" "8" +"Display.Width" "136" +"" + +"Cartridge.MD5" "9333172e3c4992ecf548d3ac1f2553eb" +"Cartridge.Name" "Strategy X" +"Cartridge.Manufacturer" "Konami / Gakken" +"Cartridge.ModelNo" "010" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "e10d2c785aadb42c06390fae0d92f282" +"Cartridge.Name" "Strawberry Shortcake Musical Match-Ups" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5910" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "27" +"Display.Height" "219" +"" + +"Cartridge.MD5" "396f7bc90ab4fa4975f8c74abe4e81f0" +"Cartridge.Name" "Street Racer / Speedway II" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2612 / 6699804 / 4975103" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Uses swapped paddles" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"Display.YStart" "34" +"" + +"Cartridge.MD5" "7b3cf0256e1fa0fdc538caf3d5d86337" +"Cartridge.Name" "Stronghold" +"Cartridge.Manufacturer" "CommaVid" +"Cartridge.ModelNo" "CM-009" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "93c52141d3c4e1b5574d072f1afde6cd" +"Cartridge.Name" "Subterranea" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "O3213" +"Cartridge.Rarity" "Rare" +"Display.YStart" "29" +"Display.Height" "205" +"" + +"Cartridge.MD5" "5af9cd346266a1f2515e1fbc86f5186a" +"Cartridge.Name" "Sub Scan" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "002-01" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "46" +"Display.Height" "192" +"" + +"Cartridge.MD5" "f3f5f72bfdd67f3d0e45d097e11b8091" +"Cartridge.Name" "Submarine Commander" +"Cartridge.Manufacturer" "Atari (available with Sears label only)" +"Cartridge.ModelNo" "CX2647 / 4975412" +"Cartridge.Rarity" "Extremely Rare Prototype / Extremely Rare" +"Display.Height" "195" +"" + +"Cartridge.MD5" "e4c666ca0c36928b95b13d33474dbb44" +"Cartridge.Name" "Suicide Mission" +"Cartridge.ModelNo" "AR-4102" +"Cartridge.Manufacturer" "Starpath (Steve Hales and Steve Landrum)" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "38" +"Display.Height" "172" +"" + +"Cartridge.MD5" "45027dde2be5bdd0cab522b80632717d" +"Cartridge.Name" "Summer Games" +"Cartridge.Manufacturer" "Epyx" +"Cartridge.ModelNo" "8056100250" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "F6" +"Display.FrameRate" "30" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "7adbcf78399b19596671edbffc3d34aa" +"Cartridge.Name" "Super Baseball" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26152" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "0ad9a358e361256b94f3fb4f2fa5a3b1" +"Cartridge.Name" "Super Breakout" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2608 / 4975165" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Cartridge.Note" "One player paddles only" +"Display.FrameRate" "30" +"Display.XStart" "8" +"Display.Width" "136" +"Display.YStart" "35" +"Display.Height" "180" +"Controller.Left" "Paddles" +"Controller.Right" "None" +"" + +"Cartridge.MD5" "9d37a1be4a6e898026414b8fee2fc826" +"Cartridge.Name" "Super Challenge Baseball" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5658" +"Cartridge.Rarity" "Common" +"Display.YStart" "38" +"Display.Height" "197" +"" + +"Cartridge.MD5" "e275cbe7d4e11e62c3bfcfb38fca3d49" +"Cartridge.Name" "Super Challenge Football" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5658" +"Cartridge.Rarity" "Common" +"Display.YStart" "38" +"Display.Height" "197" +"" + +"Cartridge.MD5" "c29f8db680990cb45ef7fef6ab57a2c2" +"Cartridge.Name" "Super Cobra" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5320" +"Cartridge.Rarity" "Uncommon" +"Display.Height" "206" +"" + +"Cartridge.MD5" "724613effaf7743cbcd695fab469c2a8" +"Cartridge.Name" "Super Ferrari" +"Cartridge.Manufacturer" "Rainbow Vision" +"Cartridge.ModelNo" "SS-011" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "09abfe9a312ce7c9f661582fdf12eab6" +"Cartridge.Name" "Super Football" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26154" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "F6SC" +"Display.XStart" "4" +"Display.Width" "152" +"Display.YStart" "42" +"Display.Height" "182" +"" + +"Cartridge.MD5" "5de8803a59c36725888346fdc6e7429d" +"Cartridge.Name" "Superman (1)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2631 / 6699845 / 4975152" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "a9531c763077464307086ec9a1fd057d" +"Cartridge.Name" "Superman (2)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2631 / 6699845 / 4975152" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "149b543c917c180a1b02d33c12415206" +"Cartridge.Name" "Superman (CCE Version)" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2631" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "4K" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "aec9b885d0e8b24e871925630884095c" +"Cartridge.Name" "Surf's Up" +"Cartridge.Manufacturer" "Amiga" +"Cartridge.ModelNo" "3125" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "17" +"Display.Height" "244" +"" + +"Cartridge.MD5" "c20f15282a1aa8724d70c117e5c9709e" +"Cartridge.Name" "Surfer's Paradise: But Danger Below!" +"Cartridge.Manufacturer" "Video Gems" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "4d7517ae69f95cfbc053be01312b7dba" +"Cartridge.Name" "Surround" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2641" +"Cartridge.Rarity" "Common" +"" + +"Cartridge.MD5" "045035f995272eb2deb8820111745a07" +"Cartridge.ModelNo" "AR-4401" +"Cartridge.Name" "Survival Island" +"Cartridge.Manufacturer" "Starpath (Scott Nelson)" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Emulation.HmoveBlanks" "No" +"Display.YStart" "38" +"Display.Height" "200" +"" + +"Cartridge.MD5" "85e564dae5687e431955056fbda10978" +"Cartridge.Name" "Survival Run" +"Cartridge.Manufacturer" "Milton Bradley" +"Cartridge.ModelNo" "4362" +"Cartridge.Rarity" "Rare" +"Display.YStart" "27" +"Display.Height" "219" +"" + +"Cartridge.MD5" "5ec73ac7d2ac95ac9530c6d33e713d14" +"Cartridge.Name" "Sweat!: The Decathlon Game" +"Cartridge.Manufacturer" "Starpath (Scott Nelson)" +"Cartridge.Rarity" "Unreleased Prototype" +"Cartridge.Type" "AR" +"Controller.Left" "Paddles" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "528400fad9a77fd5ad7fc5fdc2b7d69d" +"Cartridge.Name" "Sword of Saros" +"Cartridge.ModelNo" "AR-4201" +"Cartridge.Manufacturer" "Starpath (John Leupp)" +"Cartridge.Rarity" "Extremely Rare" +"Cartridge.Type" "AR" +"Emulation.CPU" "High" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "05ebd183ea854c0a1b56c218246fbbae" +"Cartridge.Name" "SwordQuest EarthWorld" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2656" +"Cartridge.Rarity" "Common" +"Display.YStart" "37" +"Display.Height" "195" +"" + +"Cartridge.MD5" "f9d51a4e5f8b48f68770c89ffd495ed1" +"Cartridge.Name" "SwordQuest FireWorld" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2656" +"Cartridge.Rarity" "Common" +"Display.YStart" "37" +"Display.Height" "195" +"" + +"Cartridge.MD5" "bc5389839857612cfabeb810ba7effdc" +"Cartridge.Name" "SwordQuest WaterWorld" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2671" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.YStart" "37" +"Display.Height" "205" +"" + +"Cartridge.MD5" "294762000e853b4319f9991c1ced5dfc" +"Cartridge.Name" "T.F. Space Invaders" +"Cartridge.Type" "4K" +"Cartridge.Note" "Variant of Space Invaders by Atari/Sears" +"Display.YStart" "42" +"Display.Height" "200" +"" + +"Cartridge.MD5" "d45ebf130ed9070ea8ebd56176e48a38" +"Cartridge.Name" "Tac-Scan" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "001-01" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Cartridge.Note" "One player right paddles only" +"Display.FrameRate" "60" +"Display.XStart" "8" +"Display.Width" "152" +"Display.YStart" "44" +"Display.Height" "202" +"Controller.Left" "None" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "fa6fe97a10efb9e74c0b5a816e6e1958" +"Cartridge.Name" "Tanks But No Tanks" +"Cartridge.Manufacturer" "Emag / Zimag" +"Cartridge.ModelNo" "GN-030 / 707-111" +"Cartridge.Rarity" "Extremely Rare / Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "de3d0e37729d85afcb25a8d052a6e236" +"Cartridge.Name" "Tape Worm" +"Cartridge.Manufacturer" "Spectravision" +"Cartridge.ModelNo" "SA-204" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "c0d2434348de72fa6edcc6d8e40f28d7" +"Cartridge.Name" "Tapper" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "010-01" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "32" +"" + +"Cartridge.MD5" "0c35806ff0019a270a7acae68de89d28" +"Cartridge.Name" "Task Force" +"Cartridge.Manufacturer" "Froggo" +"Cartridge.ModelNo" "FG 1003" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "a1ead9c181d67859aa93c44e40f1709c" +"Cartridge.Name" "Tax Avoiders" +"Cartridge.Manufacturer" "American Videogame" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "4702d8d9b48a332724af198aeac9e469" +"Cartridge.Name" "Taz" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2699" +"Cartridge.Rarity" "Rare" +"Display.YStart" "40" +"Display.Height" "187" +"" + +"Cartridge.MD5" "42cdd6a9e42a3639e190722b8ea3fc51" +"Cartridge.Name" "Tennis" +"Cartridge.Manufacturer" "Activision (Alan Miller)" +"Cartridge.ModelNo" "AG-007" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "2K" +"Display.FrameRate" "30" +"Display.YStart" "39" +"Display.Height" "200" +"" + +"Cartridge.MD5" "b0e1ee07fbc73493eac5651a52f90f00" +"Cartridge.Name" "Tetris26" +"Cartridge.Manufacturer" "Colin Hughes" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "5c73693a89b06e5a09f1721a13176f95" +"Cartridge.Name" "Test Cartridge" +"" + +"Cartridge.MD5" "5eeb81292992e057b290a5cd196f155d" +"Cartridge.Name" "Texas Chainsaw Massacre" +"Cartridge.Manufacturer" "Wizard Video" +"Cartridge.ModelNo" "008" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "a98b649912b6ca19eaf5c2d2faf38562" +"Cartridge.Name" "This Planet Sucks" +"Cartridge.Manufacturer" "What?Where" +"Cartridge.Rarity" "New Release" +"" + +"Cartridge.MD5" "e63a87c231ee9a506f9599aa4ef7dfb9" +"Cartridge.Name" "Threshold" +"Cartridge.Manufacturer" "Tigervision" +"Cartridge.ModelNo" "7-003" +"Cartridge.Rarity" "Rare" +"Display.YStart" "22" +"" + +"Cartridge.MD5" "cf507910d6e74568a68ac949537bccf9" +"Cartridge.Name" "Thunderground" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "003-01" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "4e99ebd65a967cabf350db54405d577c" +"Cartridge.Name" "Time Pilot (1)" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2663" +"Cartridge.Rarity" "Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "fc2104dd2dadf9a6176c1c1c8f87ced9" +"Cartridge.Name" "Time Pilot (2)" +"Cartridge.Manufacturer" "Coleco" +"Cartridge.ModelNo" "2663" +"Cartridge.Rarity" "Rare" +"Display.Height" "190" +"" + +"Cartridge.MD5" "332f01fd18e99c6584f61aa45ee7791e" +"Cartridge.Name" "Time Warp" +"Cartridge.Manufacturer" "CCE" +"Cartridge.ModelNo" "C-845" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"Display.Height" "229" +"" + +"Cartridge.MD5" "da6465a34d2e44d26aa9a2a0cd1bce4d" +"Cartridge.Name" "Title Match Pro Wrestling" +"Cartridge.Manufacturer" "Activision / Absolute Entertainment" +"Cartridge.ModelNo" "AG-041" +"Cartridge.Rarity" "Extremely Rare / Rare" +"Display.YStart" "47" +"Display.Height" "184" +"" + +"Cartridge.MD5" "ece908d77ab944f7bac84322b9973549" +"Cartridge.Name" "Tom Boy" +"Cartridge.Manufacturer" "Rainbow Vision" +"Cartridge.Note" "Variant of Pitfall by Activision" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "fa2be8125c3c60ab83e1c0fe56922fcb" +"Cartridge.Name" "Tooth Protectors" +"Cartridge.Manufacturer" "DSD / Camelot" +"Cartridge.Rarity" "Unbelievably Rare" +"Cartridge.Type" "E0" +"" + +"Cartridge.MD5" "0aa208060d7c140f20571e3341f5a3f8" +"Cartridge.Name" "Towering Inferno" +"Cartridge.Manufacturer" "U.S. Games" +"Cartridge.ModelNo" "VC 1009" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Note" "Uses swapped joysticks" +"Display.YStart" "32" +"Display.Height" "219" +"" + +"Cartridge.MD5" "6ae4dc6d7351dacd1012749ca82f9a56" +"Cartridge.Name" "Track and Field" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26125 (label says CX26127 on some)" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "4df9d7352a56a458abb7961bf10aba4e" +"Cartridge.Name" "Traffic" +"Display.YStart" "35" +"Display.Height" "208" +"" + +"Cartridge.MD5" "24df052902aa9de21c2b2525eb84a255" +"Cartridge.Name" "Trick Shot" +"Cartridge.Manufacturer" "Imagic" +"Cartridge.ModelNo" "IA3000" +"Cartridge.Rarity" "Rare" +"Display.YStart" "45" +"Display.Height" "177" +"" + +"Cartridge.MD5" "fb27afe896e7c928089307b32e5642ee" +"Cartridge.Name" "Tron Deadly Discs" +"Cartridge.Manufacturer" "M-Network (Mattel)" +"Cartridge.ModelNo" "MT5662" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.XStart" "8" +"Display.Width" "144" +"Display.YStart" "53" +"Display.Height" "162" +"" + +"Cartridge.MD5" "e17699a54c90f3a56ae4820f779f72c4" +"Cartridge.Name" "Tuby Bird" +"Cartridge.Manufacturer" "Rainbow Vision" +"Cartridge.Note" "Variant of Dolphin by Activision" +"Cartridge.ModelNo" "SS-020" +"Display.YStart" "44" +"Display.Height" "195" +"" + +"Cartridge.MD5" "b2737034f974535f5c0c6431ab8caf73" +"Cartridge.Name" "Tunnel Runner (1)" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "4L-2520" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "FASC" +"Display.FrameRate" "20" +"Display.YStart" "67" +"Display.Height" "153" +"" + +"Cartridge.MD5" "efefc02bbc5258815457f7a5b8d8750a" +"Cartridge.Name" "Tunnel Runner (2)" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "4L-2520" +"Cartridge.Rarity" "Rare" +"Cartridge.Type" "FASC" +"Display.FrameRate" "20" +"Display.YStart" "67" +"Display.Height" "153" +"" + +"Cartridge.MD5" "7a5463545dfb2dcfdafa6074b2f2c15e" +"Cartridge.Name" "Turmoil" +"Cartridge.Manufacturer" "20th Century Fox Video Games" +"Cartridge.ModelNo" "11007" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.FrameRate" "30" +"Display.YStart" "52" +"Display.Height" "186" +"" + +"Cartridge.MD5" "085322bae40d904f53bdcc56df0593fc" +"Cartridge.Name" "Tutankham" +"Cartridge.Manufacturer" "Parker Bros." +"Cartridge.ModelNo" "PB5340" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "619de46281eb2e0adbb98255732483b4" +"Cartridge.Name" "UFO Patrol" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "22" +"" + +"Cartridge.MD5" "81a010abdba1a640f7adf7f84e13d307" +"Cartridge.Name" "Universal Chaos" +"Cartridge.Manufacturer" "Telegames" +"Cartridge.ModelNo" "7062 A305" +"Cartridge.Rarity" "Rare" +"Display.YStart" "42" +"Display.Height" "190" +"" + +"Cartridge.MD5" "a499d720e7ee35c62424de882a3351b6" +"Cartridge.Name" "Up 'n' Down" +"Cartridge.Manufacturer" "Sega" +"Cartridge.ModelNo" "009-01" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "35" +"Display.Height" "205" +"" + +"Cartridge.MD5" "c6556e082aac04260596b4045bc122de" +"Cartridge.Name" "Vanguard" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2669" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "F8" +"Display.YStart" "37" +"Display.Height" "194" +"" + +"Cartridge.MD5" "3e899eba0ca8cd2972da1ae5479b4f0d" +"Cartridge.Name" "Venture" +"Cartridge.Manufacturer" "Coleco / Atari" +"Cartridge.ModelNo" "2457 / CX26145" +"Cartridge.Rarity" "Common / Rare" +"Display.YStart" "40" +"Display.Height" "195" +"" + +"Cartridge.MD5" "539d26b6e9df0da8e7465f0f5ad863b7" +"Cartridge.Name" "Video Checkers / Checkers" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2636 / 4975156" +"Cartridge.Rarity" "Rare" +"" + +"Cartridge.MD5" "f0b7db930ca0e548c41a97160b9f6275" +"Cartridge.Name" "Video Chess" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2645 / 6699817 / 4975181" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.XStart" "16" +"Display.Width" "128" +"Display.YStart" "37" +"Display.Height" "194" +"" + +"Cartridge.MD5" "4191b671bcd8237fc8e297b4947f2990" +"Cartridge.Name" "Video Jogger" +"Cartridge.Manufacturer" "Exus" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "35" +"Display.Height" "200" +"" + +"Cartridge.MD5" "60e0ea3cbe0913d39803477945e9e5ec" +"Cartridge.Name" "Video Olympics / Pong Sports" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2621" +"Cartridge.Rarity" "Common" +"Cartridge.Type" "2K" +"Display.YStart" "30" +"Display.Height" "205" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "107cc025334211e6d29da0b6be46aec7" +"Cartridge.Name" "Video Pinball / Arcade Pinball" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2648 / 4975161" +"Cartridge.Rarity" "Uncommon" +"Display.YStart" "35" +"Display.Height" "191" +"" + +"Cartridge.MD5" "ee659ae50e9df886ac4f8d7ad10d046a" +"Cartridge.Name" "Video Reflex" +"Cartridge.Manufacturer" "Exus" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "6041f400b45511aa3a69fab4b8fc8f41" +"Cartridge.Name" "Wabbit" +"Cartridge.Manufacturer" "Apollo" +"Cartridge.ModelNo" "AP 2010" +"Cartridge.Rarity" "Rare" +"Display.YStart" "38" +"Display.Height" "192" +"" + +"Cartridge.MD5" "d175258b2973b917a05b46df4e1cf15d" +"Cartridge.Name" "Walker" +"Cartridge.Note" "Variant of Clown Down Town" +"Display.Height" "219" +"" + +"Cartridge.MD5" "d3456b4cf1bd1a7b8fb907af1a80ee15" +"Cartridge.Name" "Wall Ball" +"Cartridge.Manufacturer" "Avalon Hill" +"Cartridge.ModelNo" "50030" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "c16fbfdbfdf5590cc8179e4b0f5f5aeb" +"Cartridge.Name" "Wall Defender" +"Cartridge.Manufacturer" "Home Entertainment Suppliers / Bomb" +"Cartridge.ModelNo" "CA285" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "219" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "cbe5a166550a8129a5e6d374901dffad" +"Cartridge.Name" "Warlords" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2610 / 4975127" +"Cartridge.Rarity" "Uncommon" +"Cartridge.Type" "4K" +"Display.YStart" "37" +"Display.Height" "194" +"Controller.Left" "Paddles" +"Controller.Right" "Paddles" +"" + +"Cartridge.MD5" "679e910b27406c6a2072f9569ae35fc8" +"Cartridge.Name" "Warplock" +"Cartridge.Manufacturer" "Data Age" +"Cartridge.ModelNo" "DA 1002" +"Cartridge.Rarity" "Common" +"Display.Height" "195" +"" + +"Cartridge.MD5" "9d2938eb2b17bb73e9a79bbc06053506" +"Cartridge.Name" "Wing War" +"Cartridge.Manufacturer" "Activision (Michael Greene)/ Imagic" +"Cartridge.ModelNo" "EIZ-002-04" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "200" +"Display.YStart" "52" +"" + +"Cartridge.MD5" "83fafd7bd12e3335166c6314b3bde528" +"Cartridge.Name" "Winter Games" +"Cartridge.Manufacturer" "Epyx" +"Cartridge.ModelNo" "8056100251" +"Cartridge.Rarity" "Rare" +"Display.YStart" "37" +"" + +"Cartridge.MD5" "7b24bfe1b61864e758ada1fe9adaa098" +"Cartridge.Name" "Wizard" +"Cartridge.Manufacturer" "Atari" +"Cartridge.Rarity" "Unreleased Prototype" +"Display.Height" "195" +"" + +"Cartridge.MD5" "7e8aa18bc9502eb57daaf5e7c1e94da7" +"Cartridge.Name" "Wizard of Wor" +"Cartridge.Manufacturer" "CBS Electronics" +"Cartridge.ModelNo" "M8774" +"Cartridge.Rarity" "Rare" +"Cartridge.Note" "Uses swapped joysticks" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "ec3beb6d8b5689e867bafb5d5f507491" +"Cartridge.Name" "Word Zapper" +"Cartridge.Manufacturer" "U.S. Games (Vidtec)" +"Cartridge.ModelNo" "VC 1003" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "87f020daa98d0132e98e43db7d8fea7e" +"Cartridge.Name" "Worm War I" +"Cartridge.Manufacturer" "20th Century Fox Video Games (Sirius)" +"Cartridge.ModelNo" "11001" +"Cartridge.Rarity" "Uncommon" +"" + +"Cartridge.MD5" "5961d259115e99c30b64fe7058256bcf" +"Cartridge.Name" "X-Man" +"Cartridge.Manufacturer" "Universal Gamex" +"Cartridge.ModelNo" "GX-001" +"Cartridge.Rarity" "Unbelievably Rare" +"Display.YStart" "27" +"" + +"Cartridge.MD5" "eaf744185d5e8def899950ba7c6e7bb5" +"Cartridge.Name" "Xenophobe" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX26172" +"Cartridge.Rarity" "Extremely Rare" +"" + +"Cartridge.MD5" "c6688781f4ab844852f4e3352772289b" +"Cartridge.Name" "Xevious" +"Cartridge.Manufacturer" "Atari" +"Cartridge.ModelNo" "CX2695" +"Cartridge.Rarity" "Unbelievably Rare Prototype" +"Display.Height" "210" +"Display.YStart" "26" +"" + +"Cartridge.MD5" "c5930d0e8cdae3e037349bfa08e871be" +"Cartridge.Name" "Yars' Revenge (1)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2655 / 4975167" +"Cartridge.Rarity" "Common" +"Display.Height" "193" +"Display.YStart" "36" +"" + +"Cartridge.MD5" "5f681403b1051a0822344f467b05a94d" +"Cartridge.Name" "Yars' Revenge (2)" +"Cartridge.Manufacturer" "Atari / Sears" +"Cartridge.ModelNo" "CX2655 / 4975167" +"Cartridge.Rarity" "Common" +"Display.Height" "193" +"Display.YStart" "36" +"" + +"Cartridge.MD5" "c469151655e333793472777052013f4f" +"Cartridge.Name" "Z-Tack" +"Cartridge.Manufacturer" "Bomb" +"Cartridge.ModelNo" "13" +"Cartridge.Rarity" "Extremely Rare" +"Display.YStart" "30" +"Display.Height" "205" +"" + +"Cartridge.MD5" "eea0da9b987d661264cce69a7c13c3bd" +"Cartridge.Name" "Zaxxon" +"Cartridge.Manufacturer" "Coleco / CCE / CBS Electronics" +"Cartridge.ModelNo" "2454 / C-1001 / 4L-2277" +"Cartridge.Rarity" "Uncommon / Extremely Rare / Extremely Rare" +"" + +"Cartridge.MD5" "fb833ed50c865a9a505a125fc9d79a7e" +"Cartridge.Name" "Zoo Fun" +"Cartridge.Manufacturer" "Homevision" +"Cartridge.Rarity" "Extremely Rare" +"Display.Height" "249" +"Display.YStart" "27" +"Timer.Adjustment" "1" +"" + +"Cartridge.MD5" "4565c1a7abce773e53c75b35414adefd" +"Cartridge.Name" "Non-functional STARPATH.BIN" +"" + +"Cartridge.MD5" "130c5742cd6cbe4877704d733d5b08ca" +"Cartridge.Name" "Non-functional WORLDEND.BIN" +"" + +"Cartridge.MD5" "a8916734ff8c64ec3342f4c73fd5b57d" +"Cartridge.Name" "Non-functional SALTDIAG.BIN" diff --git a/stella/src/ui/dos/PCJoys.cxx b/stella/src/ui/dos/PCJoys.cxx new file mode 100644 index 000000000..844452bd2 --- /dev/null +++ b/stella/src/ui/dos/PCJoys.cxx @@ -0,0 +1,208 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: PCJoys.cxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +//============================================================================ + +#include +#include +#include "PCJoys.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PCJoysticks::PCJoysticks(bool useAxisMidpoint) + : myGamePort(0x0201), + myUseAxisMidpoint(useAxisMidpoint) +{ + for(uInt32 i = 0; i < 4; ++i) + { + myMinimum[i] = 0x0FFFFFFF; + myMaximum[i] = 0; + myMidpoint[i] = 0; + } + + // Determine which joysticks are present + myPresent = detect(); + + // If we're using midpoints then calibrate them now + if(myUseAxisMidpoint) + { + calibrate(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +PCJoysticks::~PCJoysticks() +{ +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool PCJoysticks::present() const +{ + return myPresent; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PCJoysticks::read(bool button[4], Int16 axis[4]) +{ + // Get the state of each of the buttons + uInt8 b = (~inportb(myGamePort)) >> 4; + button[0] = b & 0x01 ? true : false; + button[1] = b & 0x02 ? true : false; + button[2] = b & 0x04 ? true : false; + button[3] = b & 0x08 ? true : false; + + // Count how long it takes each axis to change states + Int32 count[4] = {0, 0, 0, 0}; + disable(); + outportb(myGamePort, 0); + for(uInt32 counter = 0; counter < 0x000FFFFF; ++counter) + { + uInt8 i = inportb(myGamePort); + + if(i & 0x01) + ++count[0]; + if(i & 0x02) + ++count[1]; + if(i & 0x04) + ++count[2]; + if(i & 0x08) + ++count[3]; + if(!(i & myPresent)) + break; + } + enable(); + + // Determine the position of each of the axes + for(uInt32 t = 0; t < 4; ++t) + { + // Is this axis being used? + if(myPresent & (1 << t)) + { + // Update the maximum for this axis if needed + if(count[t] > myMaximum[t]) + myMaximum[t] = count[t]; + + // Update the minimum for this axis if needed + if(count[t] < myMinimum[t]) + myMinimum[t] = count[t]; + + // If the minimum and maximum aren't far enough apart then assume 0 + if(myMaximum[t] - myMinimum[t] < (myMaximum[t] >> 3)) + { + axis[t] = 0; + } + else + { + // Are we using midpoints? + if(myUseAxisMidpoint) + { + // Yes, so calculate axis value using midpoint + if(count[t] < myMidpoint[t]) + { + double n = myMidpoint[t] - count[t]; + double d = myMidpoint[t] - myMinimum[t]; + double f = ((d == 0.0) ? 0.0 : (n / d)); + if(d < (myMaximum[t] >> 3)) + axis[t] = 0; + else + axis[t] = (Int16)((Int32)(-32767L * f)); + } + else + { + double n = count[t] - myMidpoint[t]; + double d = myMaximum[t] - myMidpoint[t]; + double f = ((d == 0.0) ? 0.0 : (n / d)); + if(d < (myMaximum[t] >> 3)) + axis[t] = 0; + else + axis[t] = (Int16)((Int32)(32767L * f)); + } + } + else + { + // No, so calculate axis value without using the midpoint + double n = count[t] - myMinimum[t]; + double d = myMaximum[t] - myMinimum[t]; + double f = ((d == 0.0) ? 0.5 : (n / d)); + axis[t] = (Int16)((Int32)(65534L * f) - 32767); + } + } + } + else + { + axis[t] = 0; + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 PCJoysticks::detect() const +{ + uInt8 present = 0; + + // Reset the game port bits to 1's + outportb(myGamePort, 0); + + clock_t start = clock(); + + // Wait for low on all four joystick bits or for time to expire + do + { + present = inportb(myGamePort) & 0x0F; + } + while(((clock() - start) < (CLOCKS_PER_SEC / 2)) && present); + + // Setup joystick present mask that tells which joysticks are present + present = (present ^ 0x0F) & 0x0F; + present = present & (((present & 0x03) == 0x03) ? 0x0F : 0x0C); + present = present & (((present & 0x0C) == 0x0C) ? 0x0F : 0x03); + + return present; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void PCJoysticks::calibrate() +{ + // Make sure a joystick is present before we do a lot of work + if(present()) + { + // Count how long it takes each axis to change states + Int32 count[4] = {0, 0, 0, 0}; + disable(); + outportb(myGamePort, 0); + for(uInt32 counter = 0; counter < 0x000FFFFF; ++counter) + { + uInt8 i = inportb(myGamePort); + + if(i & 0x01) + ++count[0]; + if(i & 0x02) + ++count[1]; + if(i & 0x04) + ++count[2]; + if(i & 0x08) + ++count[3]; + if(!(i & myPresent)) + break; + } + enable(); + + for(uInt32 t = 0; t < 4; ++t) + { + myMidpoint[t] = count[t]; + } + } +} + diff --git a/stella/src/ui/dos/PCJoys.hxx b/stella/src/ui/dos/PCJoys.hxx new file mode 100644 index 000000000..d9f37bd1a --- /dev/null +++ b/stella/src/ui/dos/PCJoys.hxx @@ -0,0 +1,71 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: PCJoys.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +//============================================================================ + +#ifndef PCJOYSTICKS_HXX +#define PCJOYSTICKS_HXX + +#include "bspf.hxx" + +class PCJoysticks +{ + public: + /** + Constructor + + @param useAxisMidpoint Indicates if a "midpoints" should be used + */ + PCJoysticks(bool useAxisMidpoint); + + /** + Destructor + */ + ~PCJoysticks(); + + public: + /** + Answers true iff a joystick is connected to the system + + @return true iff a joysticks is connected + */ + bool present() const; + + /** + Read the state of the joystick and update the button and axis arrays + + @param buttons Array which holds the button state upon exit + @param axes Array which holds the axis state upon exit + */ + void read(bool buttons[4], Int16 axes[4]); + + private: + // Answers mask that indicates which joysticks are present + uInt8 detect() const; + + // Calibrate axes midpoints + void calibrate(); + + private: + uInt8 myPresent; + const uInt16 myGamePort; + const bool myUseAxisMidpoint; + Int32 myMinimum[4]; + Int32 myMaximum[4]; + Int32 myMidpoint[4]; +}; +#endif + diff --git a/stella/src/ui/dos/SndDOS.cxx b/stella/src/ui/dos/SndDOS.cxx new file mode 100644 index 000000000..e2c1d8c42 --- /dev/null +++ b/stella/src/ui/dos/SndDOS.cxx @@ -0,0 +1,129 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: SndDOS.cxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +//============================================================================ + +#include + +#include "SndDOS.hxx" +#include "TIASound.h" +#include "sbdrv.h" + +/** + Compute the buffer size to use based on the given sample rate + + @param The sample rate to compute the buffer size for +*/ +static unsigned long computeBufferSize(int sampleRate) +{ + int t; + + for(t = 7; t <= 12; ++t) + { + if((1 << t) > (sampleRate / 60)) + { + return (1 << (t - 1)); + } + } + + return 256; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SoundDOS::SoundDOS() +{ + int sampleRate = 15700; + int DMABufferSize = computeBufferSize(sampleRate); + + if(OpenSB(sampleRate, DMABufferSize)) + { + myEnabled = true; + + // Initialize TIA Sound Library + Tia_sound_init(31400, sampleRate); + + // Start playing audio + Start_audio_output(AUTO_DMA, + (void (*)(unsigned char*, short unsigned int))Tia_process); + } + else + { + // Oops, couldn't open SB so we're not enabled :-( + myEnabled = false; + return; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SoundDOS::~SoundDOS() +{ + if(myEnabled) + { + CloseSB(); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void SoundDOS::set(Sound::Register reg, uInt8 value) +{ + if(!myEnabled) + return; + + switch(reg) + { + case AUDC0: + Update_tia_sound(0x15, value); + break; + + case AUDC1: + Update_tia_sound(0x16, value); + break; + + case AUDF0: + Update_tia_sound(0x17, value); + break; + + case AUDF1: + Update_tia_sound(0x18, value); + break; + + case AUDV0: + Update_tia_sound(0x19, value); + break; + + case AUDV1: + Update_tia_sound(0x1A, value); + break; + + default: + break; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void SoundDOS::mute(bool state) +{ + if(state) + { + Stop_audio_output(); + } + else + { + Start_audio_output(AUTO_DMA, + (void (*)(unsigned char*, short unsigned int))Tia_process); + } +} + diff --git a/stella/src/ui/dos/SndDOS.hxx b/stella/src/ui/dos/SndDOS.hxx new file mode 100644 index 000000000..156adab48 --- /dev/null +++ b/stella/src/ui/dos/SndDOS.hxx @@ -0,0 +1,66 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: SndDOS.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +//============================================================================ + +#ifndef SOUNDDOS_HXX +#define SOUNDDOS_HXX + +#include "bspf.hxx" +#include "Sound.hxx" + +/** + This class implements the sound API for the DOS operating system + using a sound-blaster card. + + @author Bradford W. Mott + @version $Id: SndDOS.hxx,v 1.1.1.1 2001-12-27 19:54:32 bwmott Exp $ +*/ +class SoundDOS : public Sound +{ + public: + /** + Create a new sound object + */ + SoundDOS(); + + /** + Destructor + */ + virtual ~SoundDOS(); + + public: + /** + Set the value of the specified sound register + + @param reg The sound register to set + @param val The new value for the sound registers + */ + virtual void set(Sound::Register reg, uInt8 val); + + /** + Set the mute state of the sound object + + @param state Mutes sound iff true + */ + virtual void mute(bool state); + + private: + // Indicates if the sound system was initialized + bool myEnabled; +}; +#endif + diff --git a/stella/src/ui/dos/mainDOS.cxx b/stella/src/ui/dos/mainDOS.cxx new file mode 100644 index 000000000..65eb81762 --- /dev/null +++ b/stella/src/ui/dos/mainDOS.cxx @@ -0,0 +1,792 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: mainDOS.cxx,v 1.1.1.1 2001-12-27 19:54:33 bwmott Exp $ +//============================================================================ + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "bspf.hxx" +#include "Console.hxx" +#include "DefProps.hxx" +#include "Event.hxx" +#include "MediaSrc.hxx" +#include "PropsSet.hxx" +#include "SndDOS.hxx" +#include "System.hxx" +#include "PCJoys.hxx" +#include "scandef.h" + +// Pointer to the console object or the null pointer +Console* theConsole; + +// Event objects to use +Event theEvent; +Event theKeyboardEvent; + +// Indicates if the entire frame should be redrawn +bool theRedrawEntireFrameFlag = true; + +// Indicates if the user wants to quit +bool theQuitIndicator = false; + +// Indicates what the desired frame rate is +uInt32 theDesiredFrameRate = 60; + +// Indicate which paddle mode we're using: +// 0 - Mouse emulates paddle 0 +// 1 - Mouse emulates paddle 1 +// 2 - Mouse emulates paddle 2 +// 3 - Mouse emulates paddle 3 +// 4 - Use real Atari 2600 paddles +uInt32 thePaddleMode = 0; + +// Indicates if the Mode X graphics should be used or not +bool theUseModeXFlag = false; + +// Pointer to the joysticks object +PCJoysticks* theJoysticks; + +// Mouse IRQ +#define MOUSE_BIOS 0x33 + +// VGA Card definitions +#define VGA_BIOS 0x10 +#define VGA_PEL_ADDRESS 0x03c8 +#define VGA_PEL_DATA 0x03c9 + +// Remembers which video mode to restore when the program exits +static uInt16 theDefaultVideoMode; + +static uInt16 thePixelDataTable[256]; + +// Indicates the width and height of the screen +static uInt32 theWidth; +static uInt32 theHeight; + +// Keyboard Interrupt definitions +_go32_dpmi_seginfo theOldKeyboardHandler; +_go32_dpmi_seginfo theKeyboardHandler; +static void keyboardInterruptServiceRoutine(void); + + +/** + This routine should be called once the console is create to setup + to graphics mode +*/ +void startup() +{ + union REGS regs; + + // Get the desired width and height of the display + theWidth = theConsole->mediaSource().width(); + theHeight = theConsole->mediaSource().height(); + + // Initialize the pixel data table + for(uInt32 j = 0; j < 256; ++j) + { + thePixelDataTable[j] = j | (j << 8); + } + + // Lets save the current video mode + regs.h.ah = 0x0f; + int86(VGA_BIOS, ®s, ®s); + theDefaultVideoMode = regs.h.al; + + // Plop into 320x200x256 mode 13 + regs.w.ax = 0x0013; + regs.h.ah = 0x00; + int86(VGA_BIOS, ®s, ®s); + + // Enable Mode X if we're using it + if(theUseModeXFlag) + { + outpw(0x3C4, 0x604); // Unchain mode + outpw(0x3C4, 0xF02); + outpw(0x3D4, 0x0014); + outpw(0x3D4, 0xE317); + + outpw(0x3C2, 0xE3); // Create square pixel aspect ratio + outpw(0x3D4, 0x2C11); // Turn off write protect + outpw(0x3D4, 0x0D06); // Vertical total + outpw(0x3D4, 0x3E07); // Overflow register + outpw(0x3D4, 0xEA10); // Vertical retrace start + outpw(0x3D4, 0xAC11); // Vertical retrace end and write protect + outpw(0x3D4, 0xDF12); // Vertical display enable end + outpw(0x3D4, 0xE715); // Start vertical blanking + outpw(0x3D4, 0x0616); // End vertical blanking + + // Clear the screen now that Mode X is enabled + for(uInt32 i = 0; i < 240 * 80; ++i) + { + outp(0x3C4, 0x02); + outp(0x3C5, 0x0F); + _farpokeb(_dos_ds, 0xA0000 + i, 0); + } + } + + // Setup to color palette for the video card + const uInt32* palette = theConsole->mediaSource().palette(); + outp(VGA_PEL_ADDRESS, 0); + for(int index = 0; index < 256; index++) + { + outp(VGA_PEL_DATA, (palette[index] & 0x00ff0000) >> 18); + outp(VGA_PEL_DATA, (palette[index] & 0x0000ff00) >> 10); + outp(VGA_PEL_DATA, (palette[index] & 0x000000ff) >> 2); + } + + // Install keyboard interrupt handler + disable(); + _go32_dpmi_get_protected_mode_interrupt_vector(0x09, &theOldKeyboardHandler); + theKeyboardHandler.pm_selector = _my_cs(); + theKeyboardHandler.pm_offset = (int)keyboardInterruptServiceRoutine; + _go32_dpmi_allocate_iret_wrapper(&theKeyboardHandler); + _go32_dpmi_set_protected_mode_interrupt_vector(0x09, &theKeyboardHandler); + enable(); + + // Initialize mouse handler via DOS interrupt + regs.w.ax = 0x0000; + int86(MOUSE_BIOS, ®s, ®s); + + if(regs.w.ax == 0x0000) + { + cerr << "WARNING: Mouse initialization failed..." << endl; + } + else + { + // Set mouse bounding box to 0,0 to 511,511 + regs.w.ax = 0x0007; + regs.w.cx = 0; + regs.w.dx = 511; + int86(MOUSE_BIOS, ®s, ®s); + + regs.w.ax = 0x0008; + regs.w.cx = 0; + regs.w.dx = 511; + int86(MOUSE_BIOS, ®s, ®s); + } + + // Set joystick pointer to null + theJoysticks = 0; + + // Register function to remove interrupts when program exits + void shutdownInterrupts(); + atexit(shutdownInterrupts); +} + +/** + This function should be registered with the atexit function so that it's + automatically called when the program terminates. It's responsible for + removing any interrupts we're using. +*/ +void shutdownInterrupts() +{ + // Restore the keyboard interrupt routine + disable(); + _go32_dpmi_set_protected_mode_interrupt_vector(0x09, &theOldKeyboardHandler); + _go32_dpmi_free_iret_wrapper(&theKeyboardHandler); + enable(); +} + +/** + This function should be called right before the program exists to + clean up things and reset the graphics mode. +*/ +void shutdown() +{ + union REGS regs; + + // Restore previous display mode + regs.h.ah = 0x00; + regs.h.al = theDefaultVideoMode; + int86(VGA_BIOS, ®s, ®s); + + // Delete the joystick object + delete theJoysticks; + theJoysticks = 0; +} + +/** +*/ +void updateDisplay(MediaSource& mediaSource) +{ + uInt32* current = (uInt32*)mediaSource.currentFrameBuffer(); + uInt32* previous = (uInt32*)mediaSource.previousFrameBuffer(); + + // Are we updating a Mode X display? + if(theUseModeXFlag) + { + uInt32 width = theWidth / 4; + uInt32 height = (theHeight > 240) ? 240 : theHeight; + int offset = ((240 - height) / 2) * 80; + + // See if we can enable near pointers for updating the screen + if(__djgpp_nearptr_enable()) + { + // We've got near pointers enabled so let's use them + uInt8* data = (uInt8*)(0xA0000 + __djgpp_conventional_base + offset) + + (((160 - theWidth) / 2) * 2) / 4; + + // TODO: Rearrange this loop so we don't have to do as many calls to + // outp(). This is rather slow when the entire screen changes. + for(uInt32 y = 0; y < height; ++y) + { + uInt8* screen = data; + + for(uInt32 x = 0; x < width; ++x) + { + if(*current != *previous) + { + uInt8* frame = (uInt8*)current; + + outp(0x3C4, 0x02); + outp(0x3C5, 0x03); + *screen = *frame; + *(screen + 1) = *(frame + 2); + + outp(0x3C4, 0x02); + outp(0x3C5, 0x0C); + *screen = *(frame + 1); + *(screen + 1) = *(frame + 3); + } + screen += 2; + current++; + previous++; + } + data += 80; + } + + // Disable the near pointers + __djgpp_nearptr_disable(); + } + + // TODO: I should handle the case where near pointers aren't available + } + else + { + uInt32 width = theWidth / 4; + uInt32 height = (theHeight > 200) ? 200 : theHeight; + int offset = ((200 - height) / 2) * 320; + + // See if we can enable near pointers for updating the screen + if(__djgpp_nearptr_enable()) + { + // We've got near pointers enabled so let's use them + uInt16* data = (uInt16*)(0xA0000 + __djgpp_conventional_base + offset) + + ((160 - theWidth) / 2); + + for(uInt32 y = 0; y < height; ++y) + { + uInt16* screen = data; + data += 160; + + for(uInt32 x = 0; x < width; ++x) + { + if(*current != *previous) + { + uInt8* frame = (uInt8*)current; + + *screen++ = thePixelDataTable[*frame++]; + *screen++ = thePixelDataTable[*frame++]; + *screen++ = thePixelDataTable[*frame++]; + *screen++ = thePixelDataTable[*frame]; + } + else + { + screen += 4; + } + current++; + previous++; + } + } + + // Disable the near pointers + __djgpp_nearptr_disable(); + } + else + { + // Counldn't enable near pointers so we'll use a slower methods :-( + uInt16* data = (uInt16*)(0xA0000 + offset) + ((160 - theWidth) / 2); + + for(uInt32 y = 0; y < height; ++y) + { + uInt16* screen = data; + data += 160; + + for(uInt32 x = 0; x < width; ++x) + { + if(*current != *previous) + { + uInt8* frame = (uInt8*)current; + + _farpokew(_dos_ds, (uInt32)screen++, thePixelDataTable[*frame++]); + _farpokew(_dos_ds, (uInt32)screen++, thePixelDataTable[*frame++]); + _farpokew(_dos_ds, (uInt32)screen++, thePixelDataTable[*frame++]); + _farpokew(_dos_ds, (uInt32)screen++, thePixelDataTable[*frame++]); + } + else + { + screen += 4; + } + current++; + previous++; + } + } + } + } +} + +/** + This routine should be called regularly to handle events +*/ +void handleEvents() +{ + union REGS regs; + + // Update paddles if we're using the mouse to emulate one + if(thePaddleMode < 4) + { + // Update the paddle resistance and fire button based on the mouse settings + regs.w.ax = 0x0003; + int86(MOUSE_BIOS, ®s, ®s); + Int32 resistance = (uInt32)((1000000.0 * (512 - regs.w.cx)) / 512); + + if(thePaddleMode == 0) + { + theEvent.set(Event::PaddleZeroResistance, resistance); + theEvent.set(Event::PaddleZeroFire, (regs.w.bx & 0x07) ? 1 : 0); + } + else if(thePaddleMode == 1) + { + theEvent.set(Event::PaddleOneResistance, resistance); + theEvent.set(Event::PaddleOneFire, (regs.w.bx & 0x07) ? 1 : 0); + } + else if(thePaddleMode == 2) + { + theEvent.set(Event::PaddleTwoResistance, resistance); + theEvent.set(Event::PaddleTwoFire, (regs.w.bx & 0x07) ? 1 : 0); + } + else if(thePaddleMode == 3) + { + theEvent.set(Event::PaddleThreeResistance, resistance); + theEvent.set(Event::PaddleThreeFire, (regs.w.bx & 0x07) ? 1 : 0); + } + } + + // If no joystick object is available create one + if(theJoysticks == 0) + { + theJoysticks = new PCJoysticks(thePaddleMode != 4); + } + + if(theJoysticks->present()) + { + bool buttons[4]; + Int16 axes[4]; + + theJoysticks->read(buttons, axes); + + theEvent.set(Event::JoystickZeroFire, buttons[0] ? + 1 : theKeyboardEvent.get(Event::JoystickZeroFire)); + + theEvent.set(Event::BoosterGripZeroTrigger, buttons[1] ? + 1 : theKeyboardEvent.get(Event::BoosterGripZeroTrigger)); + + theEvent.set(Event::JoystickZeroLeft, (axes[0] < -16384) ? + 1 : theKeyboardEvent.get(Event::JoystickZeroLeft)); + + theEvent.set(Event::JoystickZeroRight, (axes[0] > 16384) ? + 1 : theKeyboardEvent.get(Event::JoystickZeroRight)); + + theEvent.set(Event::JoystickZeroUp, (axes[1] < -16384) ? + 1 : theKeyboardEvent.get(Event::JoystickZeroUp)); + + theEvent.set(Event::JoystickZeroDown, (axes[1] > 16384) ? + 1 : theKeyboardEvent.get(Event::JoystickZeroDown)); + + theEvent.set(Event::JoystickOneFire, buttons[2] ? + 1 : theKeyboardEvent.get(Event::JoystickOneFire)); + + theEvent.set(Event::BoosterGripOneTrigger, buttons[3] ? + 1 : theKeyboardEvent.get(Event::BoosterGripOneTrigger)); + + theEvent.set(Event::JoystickOneLeft, (axes[2] < -16384) ? + 1 : theKeyboardEvent.get(Event::JoystickOneLeft)); + + theEvent.set(Event::JoystickOneRight, (axes[2] > 16384) ? + 1 : theKeyboardEvent.get(Event::JoystickOneRight)); + + theEvent.set(Event::JoystickOneUp, (axes[3] < -16384) ? + 1 : theKeyboardEvent.get(Event::JoystickOneUp)); + + theEvent.set(Event::JoystickOneDown, (axes[3] > 16384) ? + 1 : theKeyboardEvent.get(Event::JoystickOneDown)); + + // If we're using real paddles then set paddle events as well + if(thePaddleMode == 4) + { + uInt32 r; + + theEvent.set(Event::PaddleZeroFire, buttons[0]); + r = (uInt32)((1.0E6L * (axes[0] + 32767L)) / 65536); + theEvent.set(Event::PaddleZeroResistance, r); + + theEvent.set(Event::PaddleOneFire, buttons[1]); + r = (uInt32)((1.0E6L * (axes[1] + 32767L)) / 65536); + theEvent.set(Event::PaddleOneResistance, r); + + theEvent.set(Event::PaddleTwoFire, buttons[2]); + r = (uInt32)((1.0E6L * (axes[2] + 32767L)) / 65536); + theEvent.set(Event::PaddleTwoResistance, r); + + theEvent.set(Event::PaddleThreeFire, buttons[3]); + r = (uInt32)((1.0E6L * (axes[3] + 32767L)) / 65536); + theEvent.set(Event::PaddleThreeResistance, r); + } + } +} + +/** + This is the keyboard interrupt service routine. It's called + whenever a key is pressed or released on the keyboard. +*/ +static void keyboardInterruptServiceRoutine(void) +{ + struct Switches + { + uInt16 scanCode; + Event::Type eventCode; + }; + + static Switches list[] = { + { SCAN_1, Event::KeyboardZero1 }, + { SCAN_2, Event::KeyboardZero2 }, + { SCAN_3, Event::KeyboardZero3 }, + { SCAN_Q, Event::KeyboardZero4 }, + { SCAN_W, Event::KeyboardZero5 }, + { SCAN_E, Event::KeyboardZero6 }, + { SCAN_A, Event::KeyboardZero7 }, + { SCAN_S, Event::KeyboardZero8 }, + { SCAN_D, Event::KeyboardZero9 }, + { SCAN_Z, Event::KeyboardZeroStar }, + { SCAN_X, Event::KeyboardZero0 }, + { SCAN_C, Event::KeyboardZeroPound }, + + { SCAN_8, Event::KeyboardOne1 }, + { SCAN_9, Event::KeyboardOne2 }, + { SCAN_0, Event::KeyboardOne3 }, + { SCAN_I, Event::KeyboardOne4 }, + { SCAN_O, Event::KeyboardOne5 }, + { SCAN_P, Event::KeyboardOne6 }, + { SCAN_K, Event::KeyboardOne7 }, + { SCAN_L, Event::KeyboardOne8 }, + { SCAN_SCOLON, Event::KeyboardOne9 }, + { SCAN_COMMA, Event::KeyboardOneStar }, + { SCAN_STOP, Event::KeyboardOne0 }, + { SCAN_FSLASH, Event::KeyboardOnePound }, + + { SCAN_DOWN, Event::JoystickZeroDown }, + { SCAN_UP, Event::JoystickZeroUp }, + { SCAN_LEFT, Event::JoystickZeroLeft }, + { SCAN_RIGHT, Event::JoystickZeroRight }, + { SCAN_SPACE, Event::JoystickZeroFire }, + { SCAN_Z, Event::BoosterGripZeroTrigger }, + { SCAN_X, Event::BoosterGripZeroBooster }, + + { SCAN_W, Event::JoystickZeroUp }, + { SCAN_S, Event::JoystickZeroDown }, + { SCAN_A, Event::JoystickZeroLeft }, + { SCAN_D, Event::JoystickZeroRight }, + { SCAN_TAB, Event::JoystickZeroFire }, + { SCAN_1, Event::BoosterGripZeroTrigger }, + { SCAN_2, Event::BoosterGripZeroBooster }, + + { SCAN_L, Event::JoystickOneDown }, + { SCAN_O, Event::JoystickOneUp }, + { SCAN_K, Event::JoystickOneLeft }, + { SCAN_SCOLON, Event::JoystickOneRight }, + { SCAN_J, Event::JoystickOneFire }, + { SCAN_N, Event::BoosterGripOneTrigger }, + { SCAN_M, Event::BoosterGripOneBooster }, + + { SCAN_F1, Event::ConsoleSelect }, + { SCAN_F2, Event::ConsoleReset }, + { SCAN_F3, Event::ConsoleColor }, + { SCAN_F4, Event::ConsoleBlackWhite }, + { SCAN_F5, Event::ConsoleLeftDifficultyA }, + { SCAN_F6, Event::ConsoleLeftDifficultyB }, + { SCAN_F7, Event::ConsoleRightDifficultyA }, + { SCAN_F8, Event::ConsoleRightDifficultyB } + }; + + // Get the scan code of the key + uInt8 scanCode = inportb(0x60); + + // If it is an 0xE0 or 0 then ignore it + if((scanCode != 0xE0) && (scanCode != 0)) + { + // See if the escape key has been pressed + if((scanCode & 0x7f) == SCAN_ESC) + { + theQuitIndicator = true; + } + else + { + // Change the event state if needed + for(unsigned int i = 0; i < sizeof(list) / sizeof(Switches); ++i) + { + if(list[i].scanCode == (scanCode & 0x7f)) + { + theEvent.set(list[i].eventCode, (scanCode & 0x80) ? 0 : 1); + theKeyboardEvent.set(list[i].eventCode, (scanCode & 0x80) ? 0 : 1); + } + } + } + } + + // Ack the interrupt + outp(0x20, 0x20); +} + +/** + Display a usage message and exit the program +*/ +void usage() +{ + static const char* message[] = { + "", + "Usage: stella [option ...] file", + "", + "Valid options are:", + "", + " -fps Display the given number of frames per second", + " -modex Use 320x240 video mode instead of 320x200", + " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate", + " or that real Atari 2600 paddles are being used", + 0 + }; + + for(uInt32 i = 0; message[i]; ++i) + { + cerr << message[i] << endl; + } + exit(1); +} + +/** + Setup the properties set by loading builtin defaults and then a + set of user specific ones from the file $HOME/.stella.pro + + @param set The properties set to setup +*/ +void setupProperties(PropertiesSet& set) +{ + // Try to load the file stella.pro file + string filename = "stella.pro"; + + // See if we can open the file + ifstream stream(filename.c_str()); + if(stream) + { + // File was opened so load properties from it + set.load(stream, &Console::defaultProperties()); + } + else + { + // Couldn't open the file so use the builtin properties file + strstream builtin; + for(const char** p = defaultPropertiesFile(); *p != 0; ++p) + { + builtin << *p << endl; + } + + set.load(builtin, &Console::defaultProperties()); + } +} + +/** + Should be called to parse the command line arguments + + @param argc The count of command line arguments + @param argv The command line arguments +*/ +void handleCommandLineArguments(int argc, char* argv[]) +{ + // Make sure we have the correct number of command line arguments + if((argc < 2) || (argc > 7)) + { + usage(); + } + + for(Int32 i = 1; i < (argc - 1); ++i) + { + // See which command line switch they're using + if(string(argv[i]) == "-fps") + { + // They're setting the desired frame rate + Int32 rate = atoi(argv[++i]); + if((rate < 1) || (rate > 300)) + { + rate = 60; + } + + theDesiredFrameRate = rate; + } + else if(string(argv[i]) == "-paddle") + { + // They're setting the desired frame rate + if(string(argv[i + 1]) == "real") + { + thePaddleMode = 4; + } + else + { + thePaddleMode = atoi(argv[i + 1]); + if((thePaddleMode < 0) || (thePaddleMode > 3)) + { + usage(); + } + } + ++i; + } + else if(string(argv[i]) == "-modex") + { + theUseModeXFlag = true; + } + else + { + usage(); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int main(int argc, char* argv[]) +{ + // Handle the command line arguments + handleCommandLineArguments(argc, argv); + + // Get a pointer to the file which contains the cartridge ROM + const char* file = argv[argc - 1]; + + // Open the cartridge image and read it in + ifstream in; + in.open(file, ios::in | ios::nocreate | ios::binary); + if(!in) + { + cerr << "ERROR: Couldn't open " << file << "..." << endl; + exit(1); + } + + uInt8* image = new uInt8[512 * 1024]; + in.read(image, 512 * 1024); + uInt32 size = in.gcount(); + in.close(); + + // Create a properties set for us to use and set it up + PropertiesSet propertiesSet("Cartridge.Name"); + setupProperties(propertiesSet); + + // Create a sound object for use with the console + SoundDOS sound; + + // Get just the filename of the file containing the ROM image + const char* filename = (!strrchr(file, '\\')) ? + file : strrchr(file, '\\') + 1; + + // Create the 2600 game console + theConsole = new Console(image, size, filename, + theEvent, propertiesSet, sound); + + // Free the image since we don't need it any longer + delete[] image; + + startup(); + + // Get the starting time in case we need to print statistics + uclock_t startingTime = uclock(); + + uInt32 numberOfFrames = 0; + for( ; !theQuitIndicator ; ++numberOfFrames) + { + // Remember the current time before we start drawing the frame + uclock_t before = uclock(); + + // Ask the media source to prepare the next frame + theConsole->mediaSource().update(); + +/* + TODO: This code seems to work fine under mode 13, however, it slows + the frame rate down under Mode X. At any point it needs to be + tested on more video cards before it's ready for production :-) + + // If we're not behind schedule then let's wait for the VSYNC! + static uclock_t endOfLastVsync = 0; + if((theDesiredFrameRate <= 60) && + (uclock() - endOfLastVsync < (1000000 / theDesiredFrameRate))) + { + while(inp(0x3DA) & 0x08); + while(!(inp(0x3DA) & 0x08)); + } + endOfLastVsync = uclock(); +*/ + + // Update the display and handle events + updateDisplay(theConsole->mediaSource()); + handleEvents(); + + // Now, waste time if we need to so that we are at the desired frame rate + for(;;) + { + uInt32 delta = uclock() - before; + + if(delta > (1000000 / theDesiredFrameRate)) + { + break; + } + } + } + + // Get the ending time in case we need to print statistics + uclock_t endingTime = uclock(); + + uInt32 scanlines = theConsole->mediaSource().scanlines(); + delete theConsole; + shutdown(); + + double executionTime = (endingTime - startingTime) / 1000000.0; + double framesPerSecond = numberOfFrames / executionTime; + + cout << endl; + cout << numberOfFrames << " total frames drawn\n"; + cout << framesPerSecond << " frames/second\n"; + cout << scanlines << " scanlines in last frame\n"; + cout << endl; +} + diff --git a/stella/src/ui/dos/sbdrv.c b/stella/src/ui/dos/sbdrv.c new file mode 100644 index 000000000..d4c46cd3c --- /dev/null +++ b/stella/src/ui/dos/sbdrv.c @@ -0,0 +1,988 @@ +/*****************************************************************************/ +/* */ +/* Module: SBDRV */ +/* Purpose: Sound Blaster DAC DMA Driver V1.3 */ +/* Author(s): Ron Fries, Neil Bradley and Bradford Mott */ +/* */ +/* 02/20/97 - Initial Release */ +/* */ +/* 08/19/97 - V1.1 - Corrected problem with the auto-detect of older SB */ +/* cards and problem with DSP shutdown which left the auto-init */ +/* mode active. Required creating a function to reset the DSP. */ +/* Also, added checks on the BLASTER settings to verify they */ +/* are possible values for either SB or SB compatibles. */ +/* Added several helpful information/error messages. These can */ +/* be disabled by removing the SBDRV_SHOW_ERR definition. */ +/* */ +/* 12/24/97 - V1.2 - Added support for DJGPP (by Bradford Mott). */ +/* */ +/* 02/04/99 - V1.3 - Cleaned up DJGPP support, locking code and data. */ +/* Fixed a bug with the reading of the BLASTER= environment */ +/* variable, which caused a segfault if one was not set. Alst */ +/* added a timeout to dsp_out() and some other minor */ +/* modifications (Matthew Conte) */ +/* */ +/*****************************************************************************/ +/* */ +/* License Information and Copyright Notice */ +/* ======================================== */ +/* */ +/* SBDrv is Copyright(c) 1997-1999 by Ron Fries, Neil Bradley and */ +/* Bradford Mott */ +/* */ +/* This library is free software; you can redistribute it and/or modify it */ +/* under the terms of version 2 of the GNU Library General Public License */ +/* as published by the Free Software Foundation. */ +/* */ +/* This library is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */ +/* General Public License for more details. */ +/* To obtain a copy of the GNU Library General Public License, write to the */ +/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* */ +/* Any permitted reproduction of these routines, in whole or in part, must */ +/* bear this legend. */ +/* */ +/*****************************************************************************/ + +#ifdef DJGPP + #include + #include + #include + + /* Handy macros (care of Allegro) to lock code / data */ + #define END_OF_FUNCTION(x) static void x##_end(void) {} + #define LOCK_VARIABLE(x) _go32_dpmi_lock_data((void*)&x,sizeof(x)) + #define LOCK_FUNCTION(x) _go32_dpmi_lock_code(x,(long)x##_end-(long)x) +#endif + +#include +#include +#include +#include +#include +#include +#include +#include "sbdrv.h" + +#define DSP_RESET 0x06 +#define DSP_READ 0x0a +#define DSP_WRITE 0x0c +#define DSP_ACK 0x0e + +#define DMA_BASE 0x00 +#define DMA_COUNT 0x01 +#define DMA_MASK 0x0a +#define DMA_MODE 0x0b +#define DMA_FF 0x0c + +#define MASTER_VOLUME 0x22 +#define LINE_VOLUME 0x2e +#define FM_VOLUME 0x26 + +/* declare local global variables */ +#ifdef DJGPP + static int theDOSBufferSegment; + static int theDOSBufferSelector; +#endif +static uint8 *Sb_buffer; +static uint16 Sb_buf_size = 200; +static uint16 Sb_offset; +static uint16 Playback_freq; +static uint8 Sb_init = 0; +static uint8 Count_low; +static uint8 Count_high; + +static uint16 IOaddr = 0x220; +static uint16 Irq = 7; +static uint16 Dma = 1; +static uint8 DMAmode = AUTO_DMA; +/*static*/ uint8 DMAcount; +static void (*FillBuffer)(uint8 *buf, uint16 buf_size); + +#ifdef DJGPP + static _go32_dpmi_seginfo OldIntVectInfo; + static _go32_dpmi_seginfo NewIntVectInfo; +#else + static void (__interrupt *OldIntVect)(void); +#endif + +/* function prototypes */ +static void setNewIntVect (uint16 irq); +static void setOldIntVect (uint16 irq); +static void dsp_out (uint16 port, uint8 val); +static uint8 hextodec (char c); +static void logErr (char *st); +static uint8 getBlasterEnv (void); +static uint8 dsp_in (uint16 port); + + +/*****************************************************************************/ +/* */ +/* Module: newIntVect */ +/* Purpose: The interrupt vector to handle the DAC DMAC completed interrupt */ +/* Sends the next buffer to the SB and re-fills the current buffer. */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ +#ifdef DJGPP +static void newIntVect(void) +#else +static void interrupt newIntVect (void) +#endif +{ + uint16 addr; + + if (DMAmode == STANDARD_DMA) + { + /* restart standard DMA transfer */ + dsp_out (IOaddr + DSP_WRITE, 0x14); + dsp_out (IOaddr + DSP_WRITE, Count_low); + dsp_out (IOaddr + DSP_WRITE, Count_high); + } + + DMAcount++; + + /* acknowledge the DSP interrupt */ + inp (IOaddr + DSP_ACK); + + /* determine the current playback position */ + addr = inp (DMA_BASE + (Dma << 1)); /* get low byte ptr */ + addr |= inp (DMA_BASE + (Dma << 1)) << 8; /* and high byte ptr */ + + addr -= Sb_offset; /* subtract the offset */ + + /* if we're currently playing the first half of the buffer */ + if (addr < Sb_buf_size) + { + /* reload the second half of the buffer */ + FillBuffer(Sb_buffer + Sb_buf_size, Sb_buf_size); + +#ifdef DJGPP + /* Copy data to DOS memory buffer */ + dosmemput(Sb_buffer + Sb_buf_size, Sb_buf_size, + (theDOSBufferSegment << 4) + Sb_buf_size); +#endif + } + else + { + /* else reload the first half of the buffer */ + FillBuffer(Sb_buffer, Sb_buf_size); + +#ifdef DJGPP + /* Copy data to DOS memory buffer */ + dosmemput(Sb_buffer, Sb_buf_size, theDOSBufferSegment << 4); +#endif + } + + /* indicate end of interrupt */ + outp (0x20, 0x20); + + if (Irq > 7) + { + outp (0xa0, 0x20); + } +} +#ifdef DJGPP +END_OF_FUNCTION(newIntVect); +#endif + +/*****************************************************************************/ +/* */ +/* Module: setNewIntVect */ +/* Purpose: To set the specified interrupt vector to the sound output */ +/* processing interrupt. */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +static void setNewIntVect (uint16 irq) +{ +#ifdef DJGPP + /* Lock code / data */ + LOCK_VARIABLE(DMAmode); + LOCK_VARIABLE(IOaddr); + LOCK_VARIABLE(Count_low); + LOCK_VARIABLE(DMAcount); + LOCK_VARIABLE(Dma); + LOCK_VARIABLE(Sb_offset); + LOCK_VARIABLE(Sb_buf_size); + LOCK_VARIABLE(theDOSBufferSegment); + LOCK_FUNCTION(newIntVect); +#endif + + if (irq > 7) + { +#ifdef DJGPP + _go32_dpmi_get_protected_mode_interrupt_vector(irq + 0x68, + &OldIntVectInfo); + NewIntVectInfo.pm_selector = _my_cs(); + NewIntVectInfo.pm_offset = (int)newIntVect; + _go32_dpmi_allocate_iret_wrapper(&NewIntVectInfo); + _go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x68, + &NewIntVectInfo); +#else + OldIntVect = _dos_getvect (irq + 0x68); + _dos_setvect (irq + 0x68, newIntVect); +#endif + } + else + { +#ifdef DJGPP + _go32_dpmi_get_protected_mode_interrupt_vector(irq + 0x08, + &OldIntVectInfo); + NewIntVectInfo.pm_selector = _my_cs(); + NewIntVectInfo.pm_offset = (int)newIntVect; + _go32_dpmi_allocate_iret_wrapper(&NewIntVectInfo); + _go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x08, + &NewIntVectInfo); +#else + OldIntVect = _dos_getvect (irq + 0x08); + _dos_setvect (irq + 0x08, newIntVect); +#endif + } +} + + +/*****************************************************************************/ +/* */ +/* Module: setOldIntVect */ +/* Purpose: To restore the original vector */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +static void setOldIntVect (uint16 irq) +{ + if (irq > 7) + { +#ifdef DJGPP + _go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x68, + &OldIntVectInfo); + _go32_dpmi_free_iret_wrapper(&NewIntVectInfo); +#else + _dos_setvect (irq + 0x68, OldIntVect); +#endif + } + else + { +#ifdef DJGPP + _go32_dpmi_set_protected_mode_interrupt_vector(irq + 0x08, + &OldIntVectInfo); + _go32_dpmi_free_iret_wrapper(&NewIntVectInfo); +#else + _dos_setvect (irq + 0x08, OldIntVect); +#endif + } +} + + +/*****************************************************************************/ +/* */ +/* Module: dsp_out */ +/* Purpose: To send a byte to the SB's DSP */ +/* Author: Ron Fries */ +/* Date: September 10, 1996 */ +/* */ +/*****************************************************************************/ + +static void dsp_out(uint16 port, uint8 val) +{ + uint32 timeout = 60000; /* set timeout */ + + /* wait for buffer to be free */ + while((timeout--) && (inp(IOaddr + DSP_WRITE) & 0x80)) + { + /* do nothing */ + } + + /* transmit the next byte */ + outp(port,val); +} + + +/*****************************************************************************/ +/* */ +/* Module: dsp_in */ +/* Purpose: To read a byte from the SB's DSP */ +/* Author: Ron Fries */ +/* Date: January 26, 1997 */ +/* */ +/*****************************************************************************/ + +static uint8 dsp_in(uint16 port) +{ + uint16 x=10000; /* set timeout */ + + /* wait for buffer to be free */ + while(((inp(IOaddr + 0x0E) & 0x80) == 0) && (x>0)) + { + /* decrement the timeout */ + x--; + } + + if (x>0) + { + /* read the data byte */ + return(inp(port)); + } + else + { + return (0); + } +} + + +/*****************************************************************************/ +/* */ +/* Module: hextodec */ +/* Purpose: Convert the input character to hex */ +/* Author: Ron Fries */ +/* Date: September 10, 1996 */ +/* */ +/*****************************************************************************/ + +uint8 hextodec (char c) +{ + uint8 retval = 0; + + c = toupper (c); + + if ((c>='0') && (c<='9')) + { + retval = c - '0'; + } + else if ((c>='A') && (c<='F')) + { + retval = c - 'A' + 10; + } + + return (retval); +} + + +/*****************************************************************************/ +/* */ +/* Module: logErr */ +/* Purpose: Displays an error message. */ +/* Author: Ron Fries */ +/* Date: September 24, 1996 */ +/* */ +/*****************************************************************************/ + +static void logErr (char *st) +{ +#ifdef SBDRV_SHOW_ERR + printf ("%s",st); +#endif +} + +/*****************************************************************************/ +/* */ +/* Module: getBlasterEnv */ +/* Purpose: Read the BLASTER environment variable and set the local globals */ +/* Author: Ron Fries */ +/* Date: September 10, 1996 */ +/* */ +/*****************************************************************************/ + +static uint8 getBlasterEnv (void) +{ + char *env; + char *ptr; + uint16 count = 0; + + env = getenv("BLASTER"); + + /* if the environment variable exists */ + if (env) + { + strupr(env); + + /* search for the address setting */ + ptr = strchr(env, 'A'); + if (ptr) + { + /* if valid, read and convert the IO address */ + IOaddr = (hextodec (ptr[1]) << 8) + + (hextodec (ptr[2]) << 4) + + (hextodec (ptr[3])); + + /* verify the IO address is one of the possible SB settings */ + switch (IOaddr) + { + case 0x210: + case 0x220: + case 0x230: + case 0x240: + case 0x250: + case 0x260: + case 0x280: + case 0x2A0: + case 0x2C0: + case 0x2E0: + /* IO address OK so indicate one more valid item found */ + count++; + break; + + default: + logErr ("Invalid Sound Blaster I/O address specified.\n"); + logErr ("Possible values are: "); + logErr ("210, 220, 230, 240, 250, 260, 280, 2A0, 2C0, 2E0.\n"); + } + } + else + { + logErr ("Unable to read Sound Blaster I/O address.\n"); + } + + /* search for the IRQ setting */ + ptr = strchr(env, 'I'); + if (ptr) + { + /* if valid, read and convert the IRQ */ + /* if the IRQ has two digits */ + if ((ptr[1] == '1') && ((ptr[2] >= '0') && (ptr[2] <='5'))) + { + /* then convert accordingly (using decimal) */ + Irq = hextodec (ptr[1]) * 10 + hextodec (ptr[2]); + } + else + { + /* else convert as a single hex digit */ + Irq = hextodec (ptr[1]); + } + + /* verify the IRQ setting is one of the possible SB settings */ + switch (Irq) + { + case 2: /* two is actually the interrupt cascade for IRQs > 7 */ + /* IRQ nine is the cascase for 2 */ + Irq = 9; + + /* IRQ OK so indicate one more valid item found */ + count++; + break; + + case 3: + case 4: + case 5: + case 7: + case 9: + case 10: + case 11: + case 12: + case 15: + + /* IRQ OK so indicate one more valid item found */ + count++; + break; + + default: + logErr ("Invalid Sound Blaster IRQ specified.\n"); + logErr ("Possible values are: "); + logErr ("2, 3, 4, 5, 7, 9, 10, 11, 12, 15.\n"); + } + } + else + { + logErr ("Unable to read Sound Blaster IRQ.\n"); + } + + /* search for the DMA setting */ + ptr = strchr(env, 'D'); + if (ptr) + { + /* if valid, read and convert the DMA */ + Dma = hextodec (ptr[1]); + + /* verify the DMA setting is one of the possible 8-bit SB settings */ + switch (Dma) + { + case 0: + case 1: + case 3: + /* DMA OK so indicate one more valid item found */ + count++; + break; + + default: + logErr ("Invalid Sound Blaster 8-bit DMA specified.\n"); + logErr ("Possible values are: "); + logErr ("0, 1, 3.\n"); + } + } + else + { + logErr ("Unable to read Sound Blaster DMA setting.\n"); + } + } + else + { + logErr ("BLASTER enviroment variable not configured."); + } + + return (count != 3); +} + + +/*****************************************************************************/ +/* */ +/* Module: low_malloc */ +/* Purpose: To allocate memory in the first 640K of memory */ +/* Author: Neil Bradley */ +/* Date: December 16, 1996 */ +/* */ +/*****************************************************************************/ + +#ifdef __WATCOMC__ +void dos_memalloc(unsigned short int para, unsigned short int *seg, unsigned short int *sel); +#pragma aux dos_memalloc = \ + "push ecx" \ + "push edx" \ + "mov ax, 0100h" \ + "int 31h" \ + "pop ebx" \ + "mov [ebx], dx" \ + "pop ebx" \ + "mov [ebx], ax" \ + parm [bx] [ecx] [edx] \ + modify [ax ebx ecx edx]; + +void dos_memfree(short int sel); +#pragma aux dos_memfree = \ + "mov ax, 0101h" \ + "int 31h" \ + parm [dx] \ + modify [ax dx]; + +void *low_malloc(int size) +{ + unsigned short int seg; + unsigned short int i=0; + + dos_memalloc((size >> 4) + 1, &seg, &i); + return((char *)(seg << 4)); +} +#endif + + +/*****************************************************************************/ +/* */ +/* Module: Set_master_volume */ +/* Purpose: To set the Sound Blaster's master volume */ +/* Author: Neil Bradley */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +void Set_master_volume(uint8 left, uint8 right) + +{ + /* if the SB was initialized */ + if (Sb_init) + { + outp(IOaddr + 0x04, MASTER_VOLUME); + outp(IOaddr + 0x05, ((left & 0xf) << 4) + (right & 0x0f)); + } +} + + +/*****************************************************************************/ +/* */ +/* Module: Set_line_volume */ +/* Purpose: To set the Sound Blaster's line level volume */ +/* Author: Neil Bradley */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +void Set_line_volume(uint8 left, uint8 right) +{ + /* if the SB was initialized */ + if (Sb_init) + { + outp(IOaddr + 0x04, LINE_VOLUME); + outp(IOaddr + 0x05, ((left & 0xf) << 4) + (right & 0x0f)); + } +} + + +/*****************************************************************************/ +/* */ +/* Module: Set_FM_volume */ +/* Purpose: To set the Sound Blaster's FM volume */ +/* Author: Neil Bradley */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +void Set_FM_volume(uint8 left, uint8 right) + +{ + /* if the SB was initialized */ + if (Sb_init) + { + outp(IOaddr + 0x04, FM_VOLUME); + outp(IOaddr + 0x05, ((left & 0xf) << 4) + (right & 0x0f)); + } +} + + +/*****************************************************************************/ +/* */ +/* Module: ResetDSP */ +/* Purpose: To reset the SB DSP. Returns a value of zero if unsuccessful. */ +/* This function requires as input the SB base port address. */ +/* Author: Ron Fries */ +/* Date: August 5, 1997 */ +/* */ +/*****************************************************************************/ + +uint8 ResetDSP(uint16 ioaddr) +{ + uint8 x; + uint16 y; + + /* assume the init was not successful */ + Sb_init = 0; + + /* send a DSP reset to the SB */ + outp (ioaddr + DSP_RESET, 1); + + /* wait a few microsec */ + x = inp(ioaddr + DSP_RESET); + x = inp(ioaddr + DSP_RESET); + x = inp(ioaddr + DSP_RESET); + x = inp(ioaddr + DSP_RESET); + + /* clear the DSP reset */ + outp (ioaddr + DSP_RESET,0); + + /* wait a bit until the SB indicates good status */ + y = 0; + + do + { + x = inp (ioaddr + DSP_READ); + y++; + } while ((y < 1000) && (x != 0xaa)); + + /* if we were able to successfully reset the SB */ + if (x == 0xaa) + { + /* turn on speaker */ + dsp_out (ioaddr + DSP_WRITE, 0xd1); + + /* read to make sure DSP register is clear */ + dsp_in (ioaddr + DSP_READ); + + /* set time constant */ + dsp_out (ioaddr + DSP_WRITE, 0x40); + dsp_out (ioaddr + DSP_WRITE, + (unsigned char)(256 - 1000000L/Playback_freq)); + + /* indicate successful initialization */ + Sb_init = 1; + } + + return (Sb_init); +} + + +/*****************************************************************************/ +/* */ +/* Module: OpenSB */ +/* Purpose: To reset the SB and prepare all buffers and other global */ +/* global variables for sound output. Allows the user to select */ +/* the playback frequency, number of buffers, and size of each */ +/* buffer. Returns a value of zero if unsuccessful. */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +uint8 OpenSB(uint16 playback_freq, uint16 buffer_size) +{ + /* initialize local globals */ + if (buffer_size > 0) + { + Sb_buf_size = buffer_size; + } + + Playback_freq = playback_freq; + + /* assume the init was not successful */ + Sb_init = 0; + + /* attempt to read the Blaster Environment Variable */ + if (getBlasterEnv() == 0) + { + /* if the DSP could be successfully reset */ + if (ResetDSP(IOaddr) != 0 ) + { + /* setup the DSP interrupt service routine */ + setNewIntVect(Irq); + + /* Enable the interrupt used */ + if (Irq > 7) + { + outp (0xa1,inp(0xa1) & (~(1<<(Irq-8)))); + } + else + { + outp (0x21,inp(0x21) & (~(1<> 4, + &theDOSBufferSelector); +#else + Sb_buffer = malloc (Sb_buf_size*2); +#endif + + /* if we were unable to successfully allocate the buffer */ +#ifdef DJGPP + if ((Sb_buffer == 0) || (theDOSBufferSegment == -1)) +#else + if (Sb_buffer == 0) +#endif + { + logErr ("Unable to allocate buffer for audio output.\n"); + + /* close the SB */ + CloseSB(); + } + } + else + { + logErr ("Unable to initialize the Sound Card.\n"); + } + + } + + return (Sb_init); +} + + +/*****************************************************************************/ +/* */ +/* Module: CloseSB */ +/* Purpose: Closes the SB and disables the interrupts. */ +/* Author: Ron Fries */ +/* Date: January 1, 1997 */ +/* */ +/*****************************************************************************/ + +void CloseSB(void) +{ +#ifdef __WATCOMC__ + uint32 addr; +#endif + + /* if the SB was initialized */ + if (Sb_init) + { + /* stop all DMA transfer */ + Stop_audio_output(); + ResetDSP(IOaddr); + + /* turn the speaker off */ + dsp_out (IOaddr + DSP_WRITE, 0xd3); + + /* indicate SB no longer active */ + Sb_init = 0; + + /* Disable the interrupt used */ + if (Irq > 7) + { + outp (0xa1,inp(0xa1) | (1<<(Irq-8))); + } + else + { + outp (0x21,inp(0x21) | (1<> 4)); +#elif defined(DJGPP) + free(Sb_buffer); + __dpmi_free_dos_memory(theDOSBufferSelector); +#else + free (Sb_buffer); +#endif + } + } +} + + +/*****************************************************************************/ +/* */ +/* Module: Stop_audio_output */ +/* Purpose: Stops the SB's DMA transfer. */ +/* Author: Ron Fries */ +/* Date: January 17, 1997 */ +/* */ +/*****************************************************************************/ + +void Stop_audio_output (void) +{ + /* stop any transfer that may be in progress */ + + /* if the SB was initialized */ + if (Sb_init) + { + /* halt DMA */ + dsp_out (IOaddr + DSP_WRITE, 0xd0); + + /* exit DMA operation*/ + dsp_out (IOaddr + DSP_WRITE, 0xda); + + /* halt DMA */ + dsp_out (IOaddr + DSP_WRITE, 0xd0); + } +} + + +/*****************************************************************************/ +/* */ +/* Module: Start_audio_output */ +/* Purpose: Fills all configured buffers and outputs the first. */ +/* Author: Ron Fries */ +/* Date: February 20, 1997 */ +/* */ +/*****************************************************************************/ + +uint8 Start_audio_output (uint8 dma_mode, + void (*fillBuffer)(uint8 *buf,uint16 n)) +{ + uint8 ret_val = 1; + static uint8 pagePort[8] = { 0x87, 0x83, 0x81, 0x82 }; + uint8 offset_low; + uint8 offset_high; + uint8 page_no; + uint8 count_low; + uint8 count_high; + uint32 addr; + clock_t start_time; + + /* if the SB initialized properly */ + if (Sb_init) + { + /* set the fill buffer routine */ + FillBuffer = fillBuffer; + + /* keep track of the DMA selection */ + DMAmode = dma_mode; + + /* stop any transfer that may be in progress */ + Stop_audio_output(); + + /* fill the buffer */ + FillBuffer (Sb_buffer, Sb_buf_size*2); + +#ifdef DJGPP + /* Copy data to DOS memory buffer */ + dosmemput(Sb_buffer, Sb_buf_size * 2, theDOSBufferSegment << 4); +#endif + + /* calculate high, low and page addresses of buffer */ +#ifdef __WATCOMC__ + addr = (uint32) Sb_buffer; +#elif defined(DJGPP) + addr = ((uint32) theDOSBufferSegment) << 4; +#else + addr = ((uint32)FP_SEG(Sb_buffer) << 4) + + (uint32)FP_OFF(Sb_buffer); +#endif + Sb_offset = (uint16)(addr & 0x0ffff); + offset_low = (uint8)(addr & 0x0ff); + offset_high = (uint8)((addr >> 8) & 0x0ff); + page_no = (uint8)(addr >> 16); + + count_low = (uint8) ((Sb_buf_size*2)-1) & 0x0ff; + count_high = (uint8) (((Sb_buf_size*2)-1) >> 8) & 0x0ff; + + /* program the DMAC for output transfer */ + outp (DMA_MASK , 0x04 | Dma ); + outp (DMA_FF , 0 ); + + /* select auto-initialize DMA mode */ + outp (DMA_MODE , 0x58 | Dma ); + outp (DMA_BASE + (Dma << 1) , offset_low ); + outp (DMA_BASE + (Dma << 1) , offset_high ); + outp (pagePort[Dma] , page_no ); + outp (DMA_COUNT + (Dma << 1), count_low ); + outp (DMA_COUNT + (Dma << 1), count_high ); + outp (DMA_MASK , Dma ); + + /* calculate the high/low buffer size counts */ + Count_low = (uint8) (Sb_buf_size-1) & 0x0ff; + Count_high = (uint8) ((Sb_buf_size-1) >> 8) & 0x0ff; + + if (DMAmode == STANDARD_DMA) + { + /* start the standard DMA transfer */ + dsp_out (IOaddr + DSP_WRITE, 0x14); + dsp_out (IOaddr + DSP_WRITE, Count_low); + dsp_out (IOaddr + DSP_WRITE, Count_high); + } + else + { + /* reset the DMA counter */ + DMAcount = 0; + + /* set the auto-initialize buffer size */ + dsp_out (IOaddr + DSP_WRITE, 0x48); + dsp_out (IOaddr + DSP_WRITE, Count_low); + dsp_out (IOaddr + DSP_WRITE, Count_high); + + /* and start the auto-initialize DMA transfer */ + dsp_out (IOaddr + DSP_WRITE, 0x1c); + + start_time = clock(); + + /* Delay for a bit and wait for DMAcount to change. */ + /* Wait for the DMA to be called twice to make sure */ + /* auto-init mode is working properly. */ + while ((clock()-start_time < (int)(CLK_TCK/2)) && (DMAcount < 2)) + { + /* This routine will wait for up to 1/2 second for DMAcount */ + /* to change. The value in CLK_TCK is the number of times */ + /* the clock will tick in one second. */ + } + + /* if the auto-init DMA is not active */ + if (DMAcount < 2) + { + /* Reset the SB DSP */ + ResetDSP(IOaddr); + + /* then try again with STANDARD_DMA */ + Start_audio_output (STANDARD_DMA, fillBuffer); + } + } + + ret_val = 0; + } + + return (ret_val); +} diff --git a/stella/src/ui/dos/sbdrv.h b/stella/src/ui/dos/sbdrv.h new file mode 100644 index 000000000..8187dbf09 --- /dev/null +++ b/stella/src/ui/dos/sbdrv.h @@ -0,0 +1,90 @@ +/*****************************************************************************/ +/* */ +/* Module: SBDRV.H */ +/* Purpose: Define function prototypes and structures required for the */ +/* SB DRV routines, V1.3. */ +/* Author(s): Ron Fries and Neil Bradley */ +/* */ +/* 01/30/97 - Initial Release */ +/* 08/24/97 - V1.1 - Added defintion of SBDRV_SHOW_ERR to cause the SBDRV */ +/* to display error messages. Comment line to supress */ +/* 01/12/98 - V1.2 - Added support for DJGPP. */ +/* 02/04/99 - V1.3 - Cleaned up DJGPP support, fixed a possible segfault */ +/* in the reading of the BLASTER= env. variable, and */ +/* added timeout to dsp_out(). */ +/* */ +/*****************************************************************************/ +/* */ +/* License Information and Copyright Notice */ +/* ======================================== */ +/* */ +/* SBDrv is Copyright(c) 1997-1999 by Ron Fries, Neil Bradley and */ +/* Bradford Mott */ +/* */ +/* This library is free software; you can redistribute it and/or modify it */ +/* under the terms of version 2 of the GNU Library General Public License */ +/* as published by the Free Software Foundation. */ +/* */ +/* This library is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */ +/* General Public License for more details. */ +/* To obtain a copy of the GNU Library General Public License, write to the */ +/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* */ +/* Any permitted reproduction of these routines, in whole or in part, must */ +/* bear this legend. */ +/* */ +/*****************************************************************************/ + +#ifndef _SBDRV_H +#define _SBDRV_H + +#ifndef _TYPEDEF_H +#define _TYPEDEF_H + +#define SBDRV_SHOW_ERR /* delete line to supress error message printing */ + +/* define some data types to keep it platform independent */ +#ifdef COMP16 /* if 16-bit compiler defined */ +#define int8 char +#define int16 int +#define int32 long +#else /* else default to 32-bit compiler */ +#define int8 char +#define int16 short +#define int32 int +#endif + +#define uint8 unsigned int8 +#define uint16 unsigned int16 +#define uint32 unsigned int32 + +#endif + +/* CONSTANT DEFINITIONS */ + +#define AUTO_DMA 0 /* selects auto-initialize DMA mode */ +#define STANDARD_DMA 1 /* selects standard DMA mode */ + +/* global function prototypes */ + +#ifdef __cplusplus +extern "C" { +#endif + +uint8 OpenSB(uint16 playback_freq, uint16 buffer_size); +void CloseSB(void); +uint8 Start_audio_output (uint8 dma_mode, + void (*fillBuffer)(uint8 *buf,uint16 n)); +void Stop_audio_output (void); + +void Set_master_volume(uint8 left, uint8 right); +void Set_line_volume(uint8 left, uint8 right); +void Set_FM_volume(uint8 left, uint8 right); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stella/src/ui/dos/sbdrv.txt b/stella/src/ui/dos/sbdrv.txt new file mode 100644 index 000000000..016b155ff --- /dev/null +++ b/stella/src/ui/dos/sbdrv.txt @@ -0,0 +1,218 @@ + SBDRV.C - SB DRIVER FUNCTIONS V1.3 + ================================== + +NOTE: There is some important information in the COMPILER REQUIREMENTS + section. + +The SBDRV.C file includes a set of simple routines for generating audio +output through a basic SB card, which should include all SB compatible cards. +The routines are designed to use the low-speed DMA output feature of the SB +card. The routines will automatically create the necessary buffers. The +audio processing/mixing function will automatically be called from the ISR +to provide a continuous stream of output. + +These functions have been designed to work with multiple compilers. +They have already been tested with Borland C++ V3.1, MSVC V1.52, +WATCOM V10.6 and DJGPP. The Borland and MSVC compilers used were 16-bit, +while the WATCOM and DJGPP were 32-bit. + +A header file 'SBDRV.H' has been included for use in other modules, and +provides the necessary function prototypes. + +The routines are easy to use. Detailed descriptions on the function calls +are listed below. + + +FEATURES +-------- + +1) Full support for IRQs 0-15. + +2) Ability to adjust the Master, FM and VOC/WAV volumes (note: not supported + by all cards). + +2) Once started, the the sound IRQ automatically calls the audio processing + function. No additional calls are required. + +3) The audio processing/mixing function is passed in as a initialization + parameter. This should allow the routines to be used in most applications + without modification. + +4) The routines support AUTO-INITIALIZE and STANDARD DMA modes of operation. + This will allow good sound reproduction on the newer cards but still + support the old cards. + + +LIMITATIONS +----------- + +The SB Driver routines have several limitations. + +1) The routines can support any DMA channel from 0 to 3. If any other DMA + channel is needed, changes are required. This may need to be changed to + support all SB cards. This should not be much of a concern, though, + since DMA channels 4-7 are only used for 16-bit DMA operation. + +2) The routines only support the low-speed DMA output function of the SB + card, which limits the maximum playback to approximately 22KHz. + +3) The routines only support 8-bit mono audio output. + +4) The routines require that the 'BLASTER' environment variable be configured. + This can be accomplished by using "SET BLASTER = A220 I7 D1" from the + command line. The 'A' value is the base address of the card, the 'I' value + is the configured interrupt line, and the 'D' value is the configured DMA + channel. + + +GENERAL OVERVIEW +---------------- + +On start-up of the system, a single call should be made to OpenSB. +This routine will reset the SB, setup the interrupts and prepare the +structures for sound output. This routine should not be called again +until after the CloseSB function is called. + +On system shut-down, a single call should be made to CloseSB. This routine +will reset the SB, disable the interrupts and free the structures that were +created. + +Once the SB has been initialized, calling the Start_audio_output function +will start the DMA operation. Once started, the sound output will be +continuous; no other calls are required. The sound output will continue +until either the Stop_audio_output or CloseSB functions are called. + +The Stop_audio_output function can be used to pause the output temporarily. + + +COMPILER REQUIREMENTS +--------------------- + +*** Probably the most important compiler option is the selection of the data +segment/stack segment relationship. Because the IRQ will be calling the +fillBuffer function from within the IRQ, the data segment and stack segment +may not be equal. The compiler should be set to DS != SS. Without this +option, a stack overflow, system lockup or other unusual problem will occur. + + +Borland C++ 3.1 Notes +--------------------- +The memory model must be set to LARGE and the COMP16 logical must be defined. +The DS/SS relationship can be left to 'default for memory model' since the +LARGE model automatically sets DS != SS, or this option can be set to never. + + +MSVC++ 1.5x +----------- +The memory model must be set to LARGE and the segment setup must be set to +'DS != SS, DS loaded on function entry'. Extern and unitialized data should +be assumed to be 'far'. Also, the COMP16 preprocessor symbol must be defined. + + +WATCOM 10.6 +------------ +The segment relationship must be set to DS != SS and stack probes must be +removed. This is accomplished by using the /zu and /s compiler options. + + +DJGPP +----- +Actually, I'm not sure what's required. I just compiled it as +GCC -o SBDRVTST.EXE SBDRVTST.C SBDRV.C and it worked. If you have problems +you may want to contact Bradford Mott as he added DJGPP support. + + +DETAILED FUNCTION DESCRIPTIONS +------------------------------ + +OpenSB (uint16 playback_freq, uint16 buffer_size) +------------------------------------------------- + +This function resets and initializes the SB, initializes and enables the +interrupts, and creates and initializes all local global structures. +This function takes two parameters: the playback frequency and the size of +each playback buffer. + +The playback frequency can be any unsigned integer from 1-65535, though +the maximum limit is currently set by the SB low-speed DMA audio routines +(approx. 22KHz). + +The system will automatically allocate two playback buffers. The size of +each buffer can be any value from 1 to 65535. The actual size needed +will depend on the playback frequency and the DMA mode. + +If Auto-Initialize DMA mode will be used, the size of the buffer can be set +as low as 35 or 40 with good output, though I don't recommend values lower +than 100. + +If Standard DMA mode will be used, the minimum size that will produce clear +output varies based on the sample frequency. At maximum freq (22kHz), the +buffer size must be between 500-600 to produce clear output. At lower sample +frequencies, the size of the buffer can be much smaller. + +If the call to OpenSB was unsuccessful, the function will return a value +of 0. Otherwise, the return value will be non-zero. + + +CloseSB (void) +-------------- + +This function disables all interrupts, frees all buffers and stops all +SB audio output. This function should be called before the program +exits. This function has no input or output parameters. + + +Set_FM_volume (uint8 left, uint8 right) +Set_line_volume (uint8 left, uint8 right) +Set_master_volume (uint8 left, uint8 right) +------------------------------------------- + +These functions set the left and right volume levels for the FM, line and +master volume controls. These functions have no output parameters (void). + + +Start_audio_output (uint8 dma_mode, void (*fillBuffer)(uint8 *buf, uint16 n)) +----------------------------------------------------------------------------- + +This function starts the audio output. It takes two parameters, the dma mode +to be used and the name of the function that generates the audio. + +The possible selections for the dma_mode are AUTO_DMA and STANDARD_DMA. +Normally, the AUTO_DMA option should always be used as the routines will +attempt to auto-detect whether the AUTO_DMA option is supported. If AUTO_DMA +is not supported, the routine will automatically switch to STANDARD_DMA mode. + +It is possible (though unlikely) that the AUTO_DMA attempt will cause +problems. Because of this possibility, the system can be forced to use the +STANDARD_DMA mode by passing STANDARD_DMA to Start_audio_output. I recommend +providing the user the option to force STANDARD_DMA operation. Note that the +buffer size may need to be increased to support STANDARD_DMA (see OpenSB). + +The fillBuffer function passed should be a function that takes two parameters, +a pointer to an unsigned 8-bit buffer and an unsigned 16-bit integer. The +fillBuffer routine should process the next 'n' bytes (specified by the 16-bit +integer) and place them in the buffer pointed to starting with byte 0. Any +function that meets these requirements can be used, which makes the SBDRV +routines generic. + + + +License Information and Copyright Notice +======================================== + +SBDRV is Copyright(c) 1997-1999 by Ron Fries, Neil Bradley and Bradford Mott + +This library is free software; you can redistribute it and/or modify it under +the terms of version 2 of the GNU Library General Public License as published +by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more +details. + +To obtain a copy of the GNU Library General Public License, write to the Free +Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Any permitted reproduction of these routines, in whole or in part, must bear +this legend. diff --git a/stella/src/ui/dos/scandef.h b/stella/src/ui/dos/scandef.h new file mode 100644 index 000000000..9c7de21f3 --- /dev/null +++ b/stella/src/ui/dos/scandef.h @@ -0,0 +1,462 @@ +/*****************************************************************************/ +/* */ +/* LIBRARY Functions - Scan code definitions Copyright K.W 1994 */ +/* */ +/*****************************************************************************/ +/* */ +/* Title : Keyboard Code Definitions */ +/* */ +/* File Name : SCANDEF.H */ +/* */ +/* Author : Keith Wilkins */ +/* */ +/* Version : 0.01 */ +/* */ +/* Desciption : This header file defines all of the scan code definitions */ +/* */ +/* Functions : None */ +/* */ +/*****************************************************************************/ +/* */ +/* Revision History: */ +/* */ +/* Version Date Who Description of changes */ +/* ------- ---- --- ---------------------- */ +/* */ +/* 0.01 13/11/94 K.W Creation */ +/* */ +/* */ +/*****************************************************************************/ + +#ifndef SCANDEF_H + #define SCANDEF_H + +#define SCAN_BASE_OFFSET 0x0000 +#define SCAN_SHIFT_OFFSET 0x0080 +#define SCAN_CTRL_OFFSET 0x0100 +#define SCAN_ALT_OFFSET 0x0180 + + +#define SCAN_BASE (SCAN_BASE_OFFSET+0x00) +#define SCAN_ESC (SCAN_BASE_OFFSET+0x01) +#define SCAN_1 (SCAN_BASE_OFFSET+0x02) +#define SCAN_2 (SCAN_BASE_OFFSET+0x03) +#define SCAN_3 (SCAN_BASE_OFFSET+0x04) +#define SCAN_4 (SCAN_BASE_OFFSET+0x05) +#define SCAN_5 (SCAN_BASE_OFFSET+0x06) +#define SCAN_6 (SCAN_BASE_OFFSET+0x07) +#define SCAN_7 (SCAN_BASE_OFFSET+0x08) +#define SCAN_8 (SCAN_BASE_OFFSET+0x09) +#define SCAN_9 (SCAN_BASE_OFFSET+0x0a) +#define SCAN_0 (SCAN_BASE_OFFSET+0x0b) +#define SCAN_MINUS (SCAN_BASE_OFFSET+0x0c) +#define SCAN_EQUALS (SCAN_BASE_OFFSET+0x0d) +#define SCAN_BSPACE (SCAN_BASE_OFFSET+0x0e) +#define SCAN_TAB (SCAN_BASE_OFFSET+0x0f) + +#define SCAN_Q (SCAN_BASE_OFFSET+0x10) +#define SCAN_W (SCAN_BASE_OFFSET+0x11) +#define SCAN_E (SCAN_BASE_OFFSET+0x12) +#define SCAN_R (SCAN_BASE_OFFSET+0x13) +#define SCAN_T (SCAN_BASE_OFFSET+0x14) +#define SCAN_Y (SCAN_BASE_OFFSET+0x15) +#define SCAN_U (SCAN_BASE_OFFSET+0x16) +#define SCAN_I (SCAN_BASE_OFFSET+0x17) +#define SCAN_O (SCAN_BASE_OFFSET+0x18) +#define SCAN_P (SCAN_BASE_OFFSET+0x19) +#define SCAN_LSQRB (SCAN_BASE_OFFSET+0x1a) +#define SCAN_RSQRB (SCAN_BASE_OFFSET+0x1b) +#define SCAN_RETURN (SCAN_BASE_OFFSET+0x1c) +#define SCAN_ENTER (SCAN_BASE_OFFSET+0x1c) +#define SCAN_SPACE (SCAN_BASE_OFFSET+0x39) +#define SCAN_BREAK (SCAN_BASE_OFFSET+0x1d) // Printed as 0x9d ??? +#define SCAN_CTRL (SCAN_BASE_OFFSET+0x1d) +#define SCAN_A (SCAN_BASE_OFFSET+0x1e) +#define SCAN_S (SCAN_BASE_OFFSET+0x1f) + +#define SCAN_D (SCAN_BASE_OFFSET+0x20) +#define SCAN_F (SCAN_BASE_OFFSET+0x21) +#define SCAN_G (SCAN_BASE_OFFSET+0x22) +#define SCAN_H (SCAN_BASE_OFFSET+0x23) +#define SCAN_J (SCAN_BASE_OFFSET+0x24) +#define SCAN_K (SCAN_BASE_OFFSET+0x25) +#define SCAN_L (SCAN_BASE_OFFSET+0x26) +#define SCAN_SCOLON (SCAN_BASE_OFFSET+0x27) +#define SCAN_APSTPY (SCAN_BASE_OFFSET+0x28) // Apostrophy ''''' +#define SCAN_TILDE (SCAN_BASE_OFFSET+0x29) +#define SCAN_LSHIFT (SCAN_BASE_OFFSET+0x2a) +#define SCAN_HASH (SCAN_BASE_OFFSET+0x2b) +#define SCAN_Z (SCAN_BASE_OFFSET+0x2c) +#define SCAN_X (SCAN_BASE_OFFSET+0x2d) +#define SCAN_C (SCAN_BASE_OFFSET+0x2e) +#define SCAN_V (SCAN_BASE_OFFSET+0x2f) + +#define SCAN_B (SCAN_BASE_OFFSET+0x30) +#define SCAN_N (SCAN_BASE_OFFSET+0x31) +#define SCAN_M (SCAN_BASE_OFFSET+0x32) +#define SCAN_COMMA (SCAN_BASE_OFFSET+0x33) +#define SCAN_STOP (SCAN_BASE_OFFSET+0x34) +#define SCAN_FSLASH (SCAN_BASE_OFFSET+0x35) +#define SCAN_RSHIFT (SCAN_BASE_OFFSET+0x36) +#define SCAN_STAR (SCAN_BASE_OFFSET+0x37) +#define SCAN_ALT (SCAN_BASE_OFFSET+0x38) + +#define SCAN_CAPS (SCAN_BASE_OFFSET+0x3a) +#define SCAN_F1 (SCAN_BASE_OFFSET+0x3b) +#define SCAN_F2 (SCAN_BASE_OFFSET+0x3c) +#define SCAN_F3 (SCAN_BASE_OFFSET+0x3d) +#define SCAN_F4 (SCAN_BASE_OFFSET+0x3e) +#define SCAN_F5 (SCAN_BASE_OFFSET+0x3f) + +#define SCAN_F6 (SCAN_BASE_OFFSET+0x40) +#define SCAN_F7 (SCAN_BASE_OFFSET+0x41) +#define SCAN_F8 (SCAN_BASE_OFFSET+0x42) +#define SCAN_F9 (SCAN_BASE_OFFSET+0x43) +#define SCAN_F10 (SCAN_BASE_OFFSET+0x44) +#define SCAN_NUMLCK (SCAN_BASE_OFFSET+0x45) +#define SCAN_SCRLCK (SCAN_BASE_OFFSET+0x46) +#define SCAN_HOME (SCAN_BASE_OFFSET+0x47) +#define SCAN_UP (SCAN_BASE_OFFSET+0x48) +#define SCAN_PGUP (SCAN_BASE_OFFSET+0x49) +#define SCAN_DASH (SCAN_BASE_OFFSET+0x4a) // Number pad minus + +#define SCAN_LEFT (SCAN_BASE_OFFSET+0x4b) +#define SCAN_CENTRE (SCAN_BASE_OFFSET+0x4c) // Number pad centre + +#define SCAN_RIGHT (SCAN_BASE_OFFSET+0x4d) +#define SCAN_PLUS (SCAN_BASE_OFFSET+0x4e) // Number pad plus + +#define SCAN_END (SCAN_BASE_OFFSET+0x4f) + +#define SCAN_DOWN (SCAN_BASE_OFFSET+0x50) +#define SCAN_PGDN (SCAN_BASE_OFFSET+0x51) +#define SCAN_INS (SCAN_BASE_OFFSET+0x52) +#define SCAN_DEL (SCAN_BASE_OFFSET+0x53) + +#define SCAN_BSLASH (SCAN_BASE_OFFSET+0x56) +#define SCAN_F11 (SCAN_BASE_OFFSET+0x57) +#define SCAN_F12 (SCAN_BASE_OFFSET+0x58) + + +// +// Scancodes with shift held +// + +#define SCAN_SHFT_BASE (SCAN_SHIFT_OFFSET+0x00) +#define SCAN_SHFT_ESC (SCAN_SHIFT_OFFSET+0x01) +#define SCAN_SHFT_1 (SCAN_SHIFT_OFFSET+0x02) +#define SCAN_SHFT_2 (SCAN_SHIFT_OFFSET+0x03) +#define SCAN_SHFT_3 (SCAN_SHIFT_OFFSET+0x04) +#define SCAN_SHFT_4 (SCAN_SHIFT_OFFSET+0x05) +#define SCAN_SHFT_5 (SCAN_SHIFT_OFFSET+0x06) +#define SCAN_SHFT_6 (SCAN_SHIFT_OFFSET+0x07) +#define SCAN_SHFT_7 (SCAN_SHIFT_OFFSET+0x08) +#define SCAN_SHFT_8 (SCAN_SHIFT_OFFSET+0x09) +#define SCAN_SHFT_9 (SCAN_SHIFT_OFFSET+0x0a) +#define SCAN_SHFT_0 (SCAN_SHIFT_OFFSET+0x0b) +#define SCAN_SHFT_MINUS (SCAN_SHIFT_OFFSET+0x0c) +#define SCAN_SHFT_EQUALS (SCAN_SHIFT_OFFSET+0x0d) +#define SCAN_SHFT_BSPACE (SCAN_SHIFT_OFFSET+0x0e) +#define SCAN_SHFT_TAB (SCAN_SHIFT_OFFSET+0x0f) + +#define SCAN_SHFT_Q (SCAN_SHIFT_OFFSET+0x10) +#define SCAN_SHFT_W (SCAN_SHIFT_OFFSET+0x11) +#define SCAN_SHFT_E (SCAN_SHIFT_OFFSET+0x12) +#define SCAN_SHFT_R (SCAN_SHIFT_OFFSET+0x13) +#define SCAN_SHFT_T (SCAN_SHIFT_OFFSET+0x14) +#define SCAN_SHFT_Y (SCAN_SHIFT_OFFSET+0x15) +#define SCAN_SHFT_U (SCAN_SHIFT_OFFSET+0x16) +#define SCAN_SHFT_I (SCAN_SHIFT_OFFSET+0x17) +#define SCAN_SHFT_O (SCAN_SHIFT_OFFSET+0x18) +#define SCAN_SHFT_P (SCAN_SHIFT_OFFSET+0x19) +#define SCAN_SHFT_LSQRB (SCAN_SHIFT_OFFSET+0x1a) +#define SCAN_SHFT_RSQRB (SCAN_SHIFT_OFFSET+0x1b) +#define SCAN_SHFT_RETURN (SCAN_SHIFT_OFFSET+0x1c) +#define SCAN_SHFT_ENTER (SCAN_SHIFT_OFFSET+0x1c) +#define SCAN_SHFT_BREAK (SCAN_SHIFT_OFFSET+0x1d) // Printed as 0x9d ??? +#define SCAN_SHFT_CTRL (SCAN_SHIFT_OFFSET+0x1d) +#define SCAN_SHFT_A (SCAN_SHIFT_OFFSET+0x1e) +#define SCAN_SHFT_S (SCAN_SHIFT_OFFSET+0x1f) + +#define SCAN_SHFT_D (SCAN_SHIFT_OFFSET+0x20) +#define SCAN_SHFT_F (SCAN_SHIFT_OFFSET+0x21) +#define SCAN_SHFT_G (SCAN_SHIFT_OFFSET+0x22) +#define SCAN_SHFT_H (SCAN_SHIFT_OFFSET+0x23) +#define SCAN_SHFT_J (SCAN_SHIFT_OFFSET+0x24) +#define SCAN_SHFT_K (SCAN_SHIFT_OFFSET+0x25) +#define SCAN_SHFT_L (SCAN_SHIFT_OFFSET+0x26) +#define SCAN_SHFT_SCOLON (SCAN_SHIFT_OFFSET+0x27) +#define SCAN_SHFT_APSTPY (SCAN_SHIFT_OFFSET+0x28) // Apostrophy ''''' +#define SCAN_SHFT_SQGL (SCAN_SHIFT_OFFSET+0x29) // ªªªªªªªª +#define SCAN_SHFT_LSHIFT (SCAN_SHIFT_OFFSET+0x2a) +#define SCAN_SHFT_HASH (SCAN_SHIFT_OFFSET+0x2b) +#define SCAN_SHFT_Z (SCAN_SHIFT_OFFSET+0x2c) +#define SCAN_SHFT_X (SCAN_SHIFT_OFFSET+0x2d) +#define SCAN_SHFT_C (SCAN_SHIFT_OFFSET+0x2e) +#define SCAN_SHFT_V (SCAN_SHIFT_OFFSET+0x2f) + +#define SCAN_SHFT_B (SCAN_SHIFT_OFFSET+0x30) +#define SCAN_SHFT_N (SCAN_SHIFT_OFFSET+0x31) +#define SCAN_SHFT_M (SCAN_SHIFT_OFFSET+0x32) +#define SCAN_SHFT_COMMA (SCAN_SHIFT_OFFSET+0x33) +#define SCAN_SHFT_STOP (SCAN_SHIFT_OFFSET+0x34) +#define SCAN_SHFT_FSLASH (SCAN_SHIFT_OFFSET+0x35) +#define SCAN_SHFT_RSHIFT (SCAN_SHIFT_OFFSET+0x36) +#define SCAN_SHFT_STAR (SCAN_SHIFT_OFFSET+0x37) +#define SCAN_SHFT_ALT (SCAN_SHIFT_OFFSET+0x38) + +#define SCAN_SHFT_CAPS (SCAN_SHIFT_OFFSET+0x3a) +#define SCAN_SHFT_F1 (SCAN_SHIFT_OFFSET+0x3b) +#define SCAN_SHFT_F2 (SCAN_SHIFT_OFFSET+0x3c) +#define SCAN_SHFT_F3 (SCAN_SHIFT_OFFSET+0x3d) +#define SCAN_SHFT_F4 (SCAN_SHIFT_OFFSET+0x3e) +#define SCAN_SHFT_F5 (SCAN_SHIFT_OFFSET+0x3f) + +#define SCAN_SHFT_F6 (SCAN_SHIFT_OFFSET+0x40) +#define SCAN_SHFT_F7 (SCAN_SHIFT_OFFSET+0x41) +#define SCAN_SHFT_F8 (SCAN_SHIFT_OFFSET+0x42) +#define SCAN_SHFT_F9 (SCAN_SHIFT_OFFSET+0x43) +#define SCAN_SHFT_F10 (SCAN_SHIFT_OFFSET+0x44) +#define SCAN_SHFT_NUMLCK (SCAN_SHIFT_OFFSET+0x45) +#define SCAN_SHFT_SCRLCK (SCAN_SHIFT_OFFSET+0x46) +#define SCAN_SHFT_HOME (SCAN_SHIFT_OFFSET+0x47) +#define SCAN_SHFT_UP (SCAN_SHIFT_OFFSET+0x48) +#define SCAN_SHFT_PGUP (SCAN_SHIFT_OFFSET+0x49) +#define SCAN_SHFT_DASH (SCAN_SHIFT_OFFSET+0x4a) // Number pad minus + +#define SCAN_SHFT_LEFT (SCAN_SHIFT_OFFSET+0x4b) +#define SCAN_SHFT_CENTRE (SCAN_SHIFT_OFFSET+0x4c) // Number pad centre + +#define SCAN_SHFT_RIGHT (SCAN_SHIFT_OFFSET+0x4d) +#define SCAN_SHFT_PLUS (SCAN_SHIFT_OFFSET+0x4e) // Number pad plus + +#define SCAN_SHFT_END (SCAN_SHIFT_OFFSET+0x4f) + +#define SCAN_SHFT_DOWN (SCAN_SHIFT_OFFSET+0x50) +#define SCAN_SHFT_PGDN (SCAN_SHIFT_OFFSET+0x51) +#define SCAN_SHFT_INS (SCAN_SHIFT_OFFSET+0x52) +#define SCAN_SHFT_DEL (SCAN_SHIFT_OFFSET+0x53) + +#define SCAN_SHFT_BSLASH (SCAN_SHIFT_OFFSET+0x56) +#define SCAN_SHFT_F11 (SCAN_SHIFT_OFFSET+0x57) +#define SCAN_SHFT_F12 (SCAN_SHIFT_OFFSET+0x58) + + + +// +// Scancodes with ctrl held +// + +#define SCAN_CTRL_BASE (SCAN_CTRL_OFFSET+0x00) +#define SCAN_CTRL_ESC (SCAN_CTRL_OFFSET+0x01) +#define SCAN_CTRL_1 (SCAN_CTRL_OFFSET+0x02) +#define SCAN_CTRL_2 (SCAN_CTRL_OFFSET+0x03) +#define SCAN_CTRL_3 (SCAN_CTRL_OFFSET+0x04) +#define SCAN_CTRL_4 (SCAN_CTRL_OFFSET+0x05) +#define SCAN_CTRL_5 (SCAN_CTRL_OFFSET+0x06) +#define SCAN_CTRL_6 (SCAN_CTRL_OFFSET+0x07) +#define SCAN_CTRL_7 (SCAN_CTRL_OFFSET+0x08) +#define SCAN_CTRL_8 (SCAN_CTRL_OFFSET+0x09) +#define SCAN_CTRL_9 (SCAN_CTRL_OFFSET+0x0a) +#define SCAN_CTRL_0 (SCAN_CTRL_OFFSET+0x0b) +#define SCAN_CTRL_MINUS (SCAN_CTRL_OFFSET+0x0c) +#define SCAN_CTRL_EQUALS (SCAN_CTRL_OFFSET+0x0d) +#define SCAN_CTRL_BSPACE (SCAN_CTRL_OFFSET+0x0e) +#define SCAN_CTRL_TAB (SCAN_CTRL_OFFSET+0x0f) + +#define SCAN_CTRL_Q (SCAN_CTRL_OFFSET+0x10) +#define SCAN_CTRL_W (SCAN_CTRL_OFFSET+0x11) +#define SCAN_CTRL_E (SCAN_CTRL_OFFSET+0x12) +#define SCAN_CTRL_R (SCAN_CTRL_OFFSET+0x13) +#define SCAN_CTRL_T (SCAN_CTRL_OFFSET+0x14) +#define SCAN_CTRL_Y (SCAN_CTRL_OFFSET+0x15) +#define SCAN_CTRL_U (SCAN_CTRL_OFFSET+0x16) +#define SCAN_CTRL_I (SCAN_CTRL_OFFSET+0x17) +#define SCAN_CTRL_O (SCAN_CTRL_OFFSET+0x18) +#define SCAN_CTRL_P (SCAN_CTRL_OFFSET+0x19) +#define SCAN_CTRL_LSQRB (SCAN_CTRL_OFFSET+0x1a) +#define SCAN_CTRL_RSQRB (SCAN_CTRL_OFFSET+0x1b) +#define SCAN_CTRL_RETURN (SCAN_CTRL_OFFSET+0x1c) +#define SCAN_CTRL_ENTER (SCAN_CTRL_OFFSET+0x1c) +#define SCAN_CTRL_BREAK (SCAN_CTRL_OFFSET+0x1d) // Printed as 0x9d ??? +#define SCAN_CTRL_CTRL (SCAN_CTRL_OFFSET+0x1d) +#define SCAN_CTRL_A (SCAN_CTRL_OFFSET+0x1e) +#define SCAN_CTRL_S (SCAN_CTRL_OFFSET+0x1f) + +#define SCAN_CTRL_D (SCAN_CTRL_OFFSET+0x20) +#define SCAN_CTRL_F (SCAN_CTRL_OFFSET+0x21) +#define SCAN_CTRL_G (SCAN_CTRL_OFFSET+0x22) +#define SCAN_CTRL_H (SCAN_CTRL_OFFSET+0x23) +#define SCAN_CTRL_J (SCAN_CTRL_OFFSET+0x24) +#define SCAN_CTRL_K (SCAN_CTRL_OFFSET+0x25) +#define SCAN_CTRL_L (SCAN_CTRL_OFFSET+0x26) +#define SCAN_CTRL_SCOLON (SCAN_CTRL_OFFSET+0x27) +#define SCAN_CTRL_APSTPY (SCAN_CTRL_OFFSET+0x28) // Apostrophy ''''' +#define SCAN_CTRL_SQGL (SCAN_CTRL_OFFSET+0x29) // ªªªªªªªª +#define SCAN_CTRL_LSHIFT (SCAN_CTRL_OFFSET+0x2a) +#define SCAN_CTRL_HASH (SCAN_CTRL_OFFSET+0x2b) +#define SCAN_CTRL_Z (SCAN_CTRL_OFFSET+0x2c) +#define SCAN_CTRL_X (SCAN_CTRL_OFFSET+0x2d) +#define SCAN_CTRL_C (SCAN_CTRL_OFFSET+0x2e) +#define SCAN_CTRL_V (SCAN_CTRL_OFFSET+0x2f) + +#define SCAN_CTRL_B (SCAN_CTRL_OFFSET+0x30) +#define SCAN_CTRL_N (SCAN_CTRL_OFFSET+0x31) +#define SCAN_CTRL_M (SCAN_CTRL_OFFSET+0x32) +#define SCAN_CTRL_COMMA (SCAN_CTRL_OFFSET+0x33) +#define SCAN_CTRL_STOP (SCAN_CTRL_OFFSET+0x34) +#define SCAN_CTRL_FSLASH (SCAN_CTRL_OFFSET+0x35) +#define SCAN_CTRL_RSHIFT (SCAN_CTRL_OFFSET+0x36) +#define SCAN_CTRL_STAR (SCAN_CTRL_OFFSET+0x37) +#define SCAN_CTRL_ALT (SCAN_CTRL_OFFSET+0x38) + +#define SCAN_CTRL_CAPS (SCAN_CTRL_OFFSET+0x3a) +#define SCAN_CTRL_F1 (SCAN_CTRL_OFFSET+0x3b) +#define SCAN_CTRL_F2 (SCAN_CTRL_OFFSET+0x3c) +#define SCAN_CTRL_F3 (SCAN_CTRL_OFFSET+0x3d) +#define SCAN_CTRL_F4 (SCAN_CTRL_OFFSET+0x3e) +#define SCAN_CTRL_F5 (SCAN_CTRL_OFFSET+0x3f) + +#define SCAN_CTRL_F6 (SCAN_CTRL_OFFSET+0x40) +#define SCAN_CTRL_F7 (SCAN_CTRL_OFFSET+0x41) +#define SCAN_CTRL_F8 (SCAN_CTRL_OFFSET+0x42) +#define SCAN_CTRL_F9 (SCAN_CTRL_OFFSET+0x43) +#define SCAN_CTRL_F10 (SCAN_CTRL_OFFSET+0x44) +#define SCAN_CTRL_NUMLCK (SCAN_CTRL_OFFSET+0x45) +#define SCAN_CTRL_SCRLCK (SCAN_CTRL_OFFSET+0x46) +#define SCAN_CTRL_HOME (SCAN_CTRL_OFFSET+0x47) +#define SCAN_CTRL_UP (SCAN_CTRL_OFFSET+0x48) +#define SCAN_CTRL_PGUP (SCAN_CTRL_OFFSET+0x49) +#define SCAN_CTRL_DASH (SCAN_CTRL_OFFSET+0x4a) // Number pad minus + +#define SCAN_CTRL_LEFT (SCAN_CTRL_OFFSET+0x4b) +#define SCAN_CTRL_CENTRE (SCAN_CTRL_OFFSET+0x4c) // Number pad centre + +#define SCAN_CTRL_RIGHT (SCAN_CTRL_OFFSET+0x4d) +#define SCAN_CTRL_PLUS (SCAN_CTRL_OFFSET+0x4e) // Number pad plus + +#define SCAN_CTRL_END (SCAN_CTRL_OFFSET+0x4f) + +#define SCAN_CTRL_DOWN (SCAN_CTRL_OFFSET+0x50) +#define SCAN_CTRL_PGDN (SCAN_CTRL_OFFSET+0x51) +#define SCAN_CTRL_INS (SCAN_CTRL_OFFSET+0x52) +#define SCAN_CTRL_DEL (SCAN_CTRL_OFFSET+0x53) + +#define SCAN_CTRL_BSLASH (SCAN_CTRL_OFFSET+0x56) +#define SCAN_CTRL_F11 (SCAN_CTRL_OFFSET+0x57) +#define SCAN_CTRL_F12 (SCAN_CTRL_OFFSET+0x58) + + + + +// +// Scancodes with alt held +// + +#define SCAN_ALT_BASE (SCAN_ALT_OFFSET+0x00) +#define SCAN_ALT_ESC (SCAN_ALT_OFFSET+0x01) +#define SCAN_ALT_1 (SCAN_ALT_OFFSET+0x02) +#define SCAN_ALT_2 (SCAN_ALT_OFFSET+0x03) +#define SCAN_ALT_3 (SCAN_ALT_OFFSET+0x04) +#define SCAN_ALT_4 (SCAN_ALT_OFFSET+0x05) +#define SCAN_ALT_5 (SCAN_ALT_OFFSET+0x06) +#define SCAN_ALT_6 (SCAN_ALT_OFFSET+0x07) +#define SCAN_ALT_7 (SCAN_ALT_OFFSET+0x08) +#define SCAN_ALT_8 (SCAN_ALT_OFFSET+0x09) +#define SCAN_ALT_9 (SCAN_ALT_OFFSET+0x0a) +#define SCAN_ALT_0 (SCAN_ALT_OFFSET+0x0b) +#define SCAN_ALT_MINUS (SCAN_ALT_OFFSET+0x0c) +#define SCAN_ALT_EQUALS (SCAN_ALT_OFFSET+0x0d) +#define SCAN_ALT_BSPACE (SCAN_ALT_OFFSET+0x0e) +#define SCAN_ALT_TAB (SCAN_ALT_OFFSET+0x0f) + +#define SCAN_ALT_Q (SCAN_ALT_OFFSET+0x10) +#define SCAN_ALT_W (SCAN_ALT_OFFSET+0x11) +#define SCAN_ALT_E (SCAN_ALT_OFFSET+0x12) +#define SCAN_ALT_R (SCAN_ALT_OFFSET+0x13) +#define SCAN_ALT_T (SCAN_ALT_OFFSET+0x14) +#define SCAN_ALT_Y (SCAN_ALT_OFFSET+0x15) +#define SCAN_ALT_U (SCAN_ALT_OFFSET+0x16) +#define SCAN_ALT_I (SCAN_ALT_OFFSET+0x17) +#define SCAN_ALT_O (SCAN_ALT_OFFSET+0x18) +#define SCAN_ALT_P (SCAN_ALT_OFFSET+0x19) +#define SCAN_ALT_LSQRB (SCAN_ALT_OFFSET+0x1a) +#define SCAN_ALT_RSQRB (SCAN_ALT_OFFSET+0x1b) +#define SCAN_ALT_RETURN (SCAN_ALT_OFFSET+0x1c) +#define SCAN_ALT_ENTER (SCAN_ALT_OFFSET+0x1c) +#define SCAN_ALT_BREAK (SCAN_ALT_OFFSET+0x1d) // Printed as 0x9d ??? +#define SCAN_ALT_CTRL (SCAN_ALT_OFFSET+0x1d) +#define SCAN_ALT_A (SCAN_ALT_OFFSET+0x1e) +#define SCAN_ALT_S (SCAN_ALT_OFFSET+0x1f) + +#define SCAN_ALT_D (SCAN_ALT_OFFSET+0x20) +#define SCAN_ALT_F (SCAN_ALT_OFFSET+0x21) +#define SCAN_ALT_G (SCAN_ALT_OFFSET+0x22) +#define SCAN_ALT_H (SCAN_ALT_OFFSET+0x23) +#define SCAN_ALT_J (SCAN_ALT_OFFSET+0x24) +#define SCAN_ALT_K (SCAN_ALT_OFFSET+0x25) +#define SCAN_ALT_L (SCAN_ALT_OFFSET+0x26) +#define SCAN_ALT_SCOLON (SCAN_ALT_OFFSET+0x27) +#define SCAN_ALT_APSTPY (SCAN_ALT_OFFSET+0x28) // Apostrophy ''''' +#define SCAN_ALT_SQGL (SCAN_ALT_OFFSET+0x29) // ªªªªªªªª +#define SCAN_ALT_LSHIFT (SCAN_ALT_OFFSET+0x2a) +#define SCAN_ALT_HASH (SCAN_ALT_OFFSET+0x2b) +#define SCAN_ALT_Z (SCAN_ALT_OFFSET+0x2c) +#define SCAN_ALT_X (SCAN_ALT_OFFSET+0x2d) +#define SCAN_ALT_C (SCAN_ALT_OFFSET+0x2e) +#define SCAN_ALT_V (SCAN_ALT_OFFSET+0x2f) + +#define SCAN_ALT_B (SCAN_ALT_OFFSET+0x30) +#define SCAN_ALT_N (SCAN_ALT_OFFSET+0x31) +#define SCAN_ALT_M (SCAN_ALT_OFFSET+0x32) +#define SCAN_ALT_COMMA (SCAN_ALT_OFFSET+0x33) +#define SCAN_ALT_STOP (SCAN_ALT_OFFSET+0x34) +#define SCAN_ALT_FSLASH (SCAN_ALT_OFFSET+0x35) +#define SCAN_ALT_RSHIFT (SCAN_ALT_OFFSET+0x36) +#define SCAN_ALT_STAR (SCAN_ALT_OFFSET+0x37) +#define SCAN_ALT_ALT (SCAN_ALT_OFFSET+0x38) + +#define SCAN_ALT_CAPS (SCAN_ALT_OFFSET+0x3a) +#define SCAN_ALT_F1 (SCAN_ALT_OFFSET+0x3b) +#define SCAN_ALT_F2 (SCAN_ALT_OFFSET+0x3c) +#define SCAN_ALT_F3 (SCAN_ALT_OFFSET+0x3d) +#define SCAN_ALT_F4 (SCAN_ALT_OFFSET+0x3e) +#define SCAN_ALT_F5 (SCAN_ALT_OFFSET+0x3f) + +#define SCAN_ALT_F6 (SCAN_ALT_OFFSET+0x40) +#define SCAN_ALT_F7 (SCAN_ALT_OFFSET+0x41) +#define SCAN_ALT_F8 (SCAN_ALT_OFFSET+0x42) +#define SCAN_ALT_F9 (SCAN_ALT_OFFSET+0x43) +#define SCAN_ALT_F10 (SCAN_ALT_OFFSET+0x44) +#define SCAN_ALT_NUMLCK (SCAN_ALT_OFFSET+0x45) +#define SCAN_ALT_SCRLCK (SCAN_ALT_OFFSET+0x46) +#define SCAN_ALT_HOME (SCAN_ALT_OFFSET+0x47) +#define SCAN_ALT_UP (SCAN_ALT_OFFSET+0x48) +#define SCAN_ALT_PGUP (SCAN_ALT_OFFSET+0x49) +#define SCAN_ALT_DASH (SCAN_ALT_OFFSET+0x4a) // Number pad minus + +#define SCAN_ALT_LEFT (SCAN_ALT_OFFSET+0x4b) +#define SCAN_ALT_CENTRE (SCAN_ALT_OFFSET+0x4c) // Number pad centre + +#define SCAN_ALT_RIGHT (SCAN_ALT_OFFSET+0x4d) +#define SCAN_ALT_PLUS (SCAN_ALT_OFFSET+0x4e) // Number pad plus + +#define SCAN_ALT_END (SCAN_ALT_OFFSET+0x4f) + +#define SCAN_ALT_DOWN (SCAN_ALT_OFFSET+0x50) +#define SCAN_ALT_PGDN (SCAN_ALT_OFFSET+0x51) +#define SCAN_ALT_INS (SCAN_ALT_OFFSET+0x52) +#define SCAN_ALT_DEL (SCAN_ALT_OFFSET+0x53) + +#define SCAN_ALT_BSLASH (SCAN_ALT_OFFSET+0x56) +#define SCAN_ALT_F11 (SCAN_ALT_OFFSET+0x57) +#define SCAN_ALT_F12 (SCAN_ALT_OFFSET+0x58) + + + + + +#endif + + +/* END OF SCANDEF.H */ diff --git a/stella/src/ui/sound/OSS.c b/stella/src/ui/sound/OSS.c new file mode 100644 index 000000000..5ca749971 --- /dev/null +++ b/stella/src/ui/sound/OSS.c @@ -0,0 +1,227 @@ +/*============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: OSS.c,v 1.1.1.1 2001-12-27 19:54:35 bwmott Exp $ +//==========================================================================*/ + +/** + This file implements the "stella-sound" process for the + Open Sound System (OSS) API. + + @author Bradford W. Mott + @version $Id: OSS.c,v 1.1.1.1 2001-12-27 19:54:35 bwmott Exp $ +*/ + +#include +#include +#include +#include +#include +#include +#include + +#ifdef __FreeBSD__ + #include +#else + #include +#endif + +#include "TIASound.h" + +/** + Compute Fragment size to use based on the sample rate + + @param sampleRate The sample rate to compute the fragment size for +*/ +unsigned long computeFragmentSize(int sampleRate); + + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +int main(int argc, char* argv[]) +{ + int fd; + int numberAndSizeOfFragments; + int fragmentSize; + unsigned char* fragmentBuffer; + int sampleRate; + int format; + int stereo; + int mute = 0; + + /* Open the sound device for writing */ + if((fd = open("/dev/dsp", O_WRONLY, 0)) == -1) + { + printf("stella-sound: Unable to open /dev/dsp device!\n"); + return 1; + } + + /* Set the AUDIO DATA FORMAT */ + format = AFMT_U8; + if(ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1) + { + printf("stella-sound: Unable to set 8-bit sample mode!\n"); + return 1; + } + + if(format != AFMT_U8) + { + printf("stella-sound: Sound card doesn't support 8-bit sample mode!\n"); + return 1; + } + + /* Set MONO MODE */ + stereo = 0; + if(ioctl(fd, SNDCTL_DSP_STEREO, &stereo) == -1) + { + printf("stella-sound: Sound card doesn't support mono mode!\n"); + return 1; + } + + if(stereo != 0) + { + printf("stella-sound: Sound card doesn't support mono mode!\n"); + return 1; + } + + /* Set the SAMPLE RATE */ + sampleRate = 31400; + if(ioctl(fd, SNDCTL_DSP_SPEED, &sampleRate) == -1) + { + printf("stella-sound: Unable to set sample rate for /dev/dsp!\n"); + return 1; + } + + /* Set the NUMBER AND SIZE OF FRAGMENTS */ + numberAndSizeOfFragments = 0x00020000 | computeFragmentSize(sampleRate); + if(ioctl(fd, SNDCTL_DSP_SETFRAGMENT, &numberAndSizeOfFragments) == -1) + { + printf("stella-sound: Unable to set fragment size!\n"); + return 1; + } + + /* Query for the actual fragment size */ + ioctl(fd, SNDCTL_DSP_GETBLKSIZE, &fragmentSize); + + /* Allocate fragment buffer */ + fragmentBuffer = (unsigned char*)malloc(fragmentSize); + + + /* Initialize the TIA Sound Library */ + Tia_sound_init(31400, sampleRate); + + + /* Make sure STDIN is in nonblocking mode */ + if(fcntl(0, F_SETFL, O_NONBLOCK) == -1) + { + printf("stella-sound: Couldn't set non-blocking mode\n"); + return 1; + } + + /* Loop reading commands from the emulator and playing sound fragments */ + for(;;) + { + int done = 0; + + while(!done) + { + int i; + int n; + unsigned char input[1024]; + + /* Read as many commands as available */ + n = read(0, input, 1024); + + /* Process all of the commands we read */ + for(i = 0; i < n; ++i) + { + unsigned char value = input[i]; + + switch((value >> 5) & 0x07) + { + case 0: /* Set AUDC0 */ + Update_tia_sound(0x15, value); + break; + + case 1: /* Set AUDC1 */ + Update_tia_sound(0x16, value); + break; + + case 2: /* Set AUDF0 */ + Update_tia_sound(0x17, value); + break; + + case 3: /* Set AUDF1 */ + Update_tia_sound(0x18, value); + break; + + case 4: /* Set AUDV0 */ + Update_tia_sound(0x19, value); + break; + + case 5: /* Set AUDV1 */ + Update_tia_sound(0x1A, value); + break; + + case 6: /* Quit */ + close(fd); + return 1; + break; + + case 7: /* Change mute command */ + mute = value & 0x01; + break; + + default: + break; + } + } + done = (n != 1024); + } + + /* If sound isn't muted then play something */ + if(!mute) + { + /* Create the next fragment to play */ + Tia_process(fragmentBuffer, fragmentSize); + + /* Write fragment to sound device */ + write(fd, fragmentBuffer, fragmentSize); + } + else + { + /* Sound is muted so let's sleep for a little while */ + struct timeval timeout; + timeout.tv_sec = 0; + timeout.tv_usec = 10000; + select(0, 0, 0, 0, &timeout); + } + } +} + +/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ +unsigned long computeFragmentSize(int sampleRate) +{ + int t; + + for(t = 7; t < 24; ++t) + { + if((1 << t) > (sampleRate / 60)) + return t - 1; + } + + /* Default to 256 byte fragment size */ + return 8; +} + diff --git a/stella/src/ui/sound/SndUnix.cxx b/stella/src/ui/sound/SndUnix.cxx new file mode 100644 index 000000000..aad327197 --- /dev/null +++ b/stella/src/ui/sound/SndUnix.cxx @@ -0,0 +1,234 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: SndUnix.cxx,v 1.1.1.1 2001-12-27 19:54:35 bwmott Exp $ +//============================================================================ + +#include +#include +#include +#include + +#include "SndUnix.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SoundUnix::SoundUnix() + : myDisabled(false), + myMute(false) +{ + // Initialize to impossible values so they will be reset + // on the first call to the set method + myAUDC0 = myAUDC1 = myAUDF0 = myAUDF1 = myAUDV0 = myAUDV1 = 255; + + int pfd[2]; + + // Create pipe for interprocess communication + if(pipe(pfd)) + { + // Oops. We were not able to create pipe so disable myself and return + myDisabled = true; + return; + } + + // Create new process, setup pipe, and start stella-sound + myProcessId = fork(); + + if(myProcessId == 0) + { + // Close STDIN and put the read end of the pipe in its place + dup2(pfd[0], 0); + + // Close unused file descriptors in child process + close(pfd[0]); + close(pfd[1]); + + // Execute the stella-sound server + if(execlp("stella-sound", "stella-sound", (char*)0)) + { + exit(1); + } + } + else if(myProcessId > 0) + { + // Close unused file descriptors in parent process + close(pfd[0]); + + // Save the pipe's write file descriptor + myFd = pfd[1]; + } + else + { + // Couldn't fork so cleanup and disabled myself + close(pfd[0]); + close(pfd[1]); + + myDisabled = true; + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +SoundUnix::~SoundUnix() +{ + if(!myDisabled) + { + // Send quit command to the sound server + unsigned char command = 0xC0; + write(myFd, &command, 1); + + // Kill the sound server + kill(myProcessId, SIGHUP); + + // Close descriptors + close(myFd); + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void SoundUnix::set(Sound::Register reg, uInt8 value) +{ + // Return if I'm currently disabled or if the stella-sound process has died + if(myDisabled || (waitpid(myProcessId, 0, WNOHANG) == myProcessId)) + { + myDisabled = true; + return; + } + + uInt8 command; + + switch(reg) + { + case AUDC0: + { + if(myAUDC0 != (value & 0x0f)) + { + myAUDC0 = (value & 0x0f); + command = myAUDC0 | 0x00; + } + else + { + return; + } + break; + } + + case AUDC1: + { + if(myAUDC1 != (value & 0x0f)) + { + myAUDC1 = (value & 0x0f); + command = myAUDC1 | 0x20; + } + else + { + return; + } + break; + } + + case AUDF0: + { + if(myAUDF0 != (value & 0x1f)) + { + myAUDF0 = (value & 0x1f); + command = myAUDF0 | 0x40; + } + else + { + return; + } + break; + } + + case AUDF1: + { + if(myAUDF1 != (value & 0x1f)) + { + myAUDF1 = (value & 0x1f); + command = myAUDF1 | 0x60; + } + else + { + return; + } + break; + } + + case AUDV0: + { + if(myAUDV0 != (value & 0x0f)) + { + myAUDV0 = (value & 0x0f); + command = myAUDV0 | 0x80; + } + else + { + return; + } + break; + } + + case AUDV1: + { + if(myAUDV1 != (value & 0x0f)) + { + myAUDV1 = (value & 0x0f); + command = myAUDV1 | 0xA0; + } + else + { + return; + } + break; + } + + default: + { + return; + break; + } + } + + // Send sound command to the stella-sound process + write(myFd, &command, 1); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void SoundUnix::mute(bool state) +{ + // Return if I'm currently disabled or if the stella-sound process has died + if(myDisabled || (waitpid(myProcessId, 0, WNOHANG) == myProcessId)) + { + myDisabled = true; + return; + } + + myMute = state; + + uInt8 command; + if(myMute) + { + // Setup the mute enable command + command = 0x01 | 0xE0; + } + else + { + // Setup the mute disable command + command = 0x00 | 0xE0; + } + + // Send sound command to the stella-sound process + write(myFd, &command, 1); +} + diff --git a/stella/src/ui/sound/SndUnix.hxx b/stella/src/ui/sound/SndUnix.hxx new file mode 100644 index 000000000..fd6aef8d6 --- /dev/null +++ b/stella/src/ui/sound/SndUnix.hxx @@ -0,0 +1,87 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1998 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: SndUnix.hxx,v 1.1.1.1 2001-12-27 19:54:35 bwmott Exp $ +//============================================================================ + +#ifndef SOUNDUNIX_HXX +#define SOUNDUNIX_HXX + +#include "bspf.hxx" +#include "Sound.hxx" + +/** + This class implements the sound API for the Unix operating system. + Under Unix the real work of the sound system is in another process + called "stella-sound". This process is started when an instance + of the SoundUnix class is created. Communicattion with the + "stella-sound" process is done through a pipe. + + @author Bradford W. Mott + @version $Id: SndUnix.hxx,v 1.1.1.1 2001-12-27 19:54:35 bwmott Exp $ +*/ +class SoundUnix : public Sound +{ + public: + /** + Create a new sound object + */ + SoundUnix(); + + /** + Destructor + */ + virtual ~SoundUnix(); + + public: + /** + Set the value of the specified sound register + + @param reg The sound register to set + @param val The new value for the sound register + */ + virtual void set(Sound::Register reg, uInt8 val); + + /** + Set the mute state of the sound object + + @param state Mutes sound iff true + */ + virtual void mute(bool state); + + private: + // Indicates if the sound system couldn't be initialized + bool myDisabled; + + // Indicates if the sound is muted or not + bool myMute; + + // ProcessId of the stella-sound process + int myProcessId; + + // Write file descriptor for IPC + int myFd; + + // Buffers for the audio registers used so we only + // send "changes" to the stella-sound process + uInt8 myAUDC0; + uInt8 myAUDC1; + uInt8 myAUDF0; + uInt8 myAUDF1; + uInt8 myAUDV0; + uInt8 myAUDV1; +}; +#endif + diff --git a/stella/src/ui/sound/TIASound.c b/stella/src/ui/sound/TIASound.c new file mode 100644 index 000000000..726f1d6aa --- /dev/null +++ b/stella/src/ui/sound/TIASound.c @@ -0,0 +1,612 @@ +/*****************************************************************************/ +/* */ +/* Module: TIA Chip Sound Simulator */ +/* Purpose: To emulate the sound generation hardware of the Atari TIA chip. */ +/* Author: Ron Fries */ +/* */ +/* Revision History: */ +/* 10-Sep-96 - V1.0 - Initial Release */ +/* 14-Jan-97 - V1.1 - Cleaned up sound output by eliminating counter */ +/* reset. */ +/* */ +/*****************************************************************************/ +/* */ +/* License Information and Copyright Notice */ +/* ======================================== */ +/* */ +/* TiaSound is Copyright(c) 1996 by Ron Fries */ +/* */ +/* This library is free software; you can redistribute it and/or modify it */ +/* under the terms of version 2 of the GNU Library General Public License */ +/* as published by the Free Software Foundation. */ +/* */ +/* This library is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */ +/* General Public License for more details. */ +/* To obtain a copy of the GNU Library General Public License, write to the */ +/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* */ +/* Any permitted reproduction of these routines, in whole or in part, must */ +/* bear this legend. */ +/* */ +/*****************************************************************************/ + +#include +#include +#include + + +/* define some data types to keep it platform independent */ +#ifdef WIN32 +#define int8 char +#define int16 short +#define int32 int +#else +#define int8 char +#define int16 int +#define int32 long +#endif + +#define uint8 unsigned int8 +#define uint16 unsigned int16 +#define uint32 unsigned int32 + + +/* CONSTANT DEFINITIONS */ + +/* definitions for AUDCx (15, 16) */ +#define SET_TO_1 0x00 /* 0000 */ +#define POLY4 0x01 /* 0001 */ +#define DIV31_POLY4 0x02 /* 0010 */ +#define POLY5_POLY4 0x03 /* 0011 */ +#define PURE 0x04 /* 0100 */ +#define PURE2 0x05 /* 0101 */ +#define DIV31_PURE 0x06 /* 0110 */ +#define POLY5_2 0x07 /* 0111 */ +#define POLY9 0x08 /* 1000 */ +#define POLY5 0x09 /* 1001 */ +#define DIV31_POLY5 0x0a /* 1010 */ +#define POLY5_POLY5 0x0b /* 1011 */ +#define DIV3_PURE 0x0c /* 1100 */ +#define DIV3_PURE2 0x0d /* 1101 */ +#define DIV93_PURE 0x0e /* 1110 */ +#define DIV3_POLY5 0x0f /* 1111 */ + +#define DIV3_MASK 0x0c + +#define AUDC0 0x15 +#define AUDC1 0x16 +#define AUDF0 0x17 +#define AUDF1 0x18 +#define AUDV0 0x19 +#define AUDV1 0x1a + +/* the size (in entries) of the 4 polynomial tables */ +#define POLY4_SIZE 0x000f +#define POLY5_SIZE 0x001f +#define POLY9_SIZE 0x01ff + +/* channel definitions */ +#define CHAN1 0 +#define CHAN2 1 + +#define FALSE 0 +#define TRUE 1 + + +/* LOCAL GLOBAL VARIABLE DEFINITIONS */ + +/* structures to hold the 6 tia sound control bytes */ +static uint8 AUDC[2]; /* AUDCx (15, 16) */ +static uint8 AUDF[2]; /* AUDFx (17, 18) */ +static uint8 AUDV[2]; /* AUDVx (19, 1A) */ + +static uint8 Outvol[2]; /* last output volume for each channel */ + + +/* Initialze the bit patterns for the polynomials. */ + +/* The 4bit and 5bit patterns are the identical ones used in the tia chip. */ +/* Though the patterns could be packed with 8 bits per byte, using only a */ +/* single bit per byte keeps the math simple, which is important for */ +/* efficient processing. */ + +static uint8 Bit4[POLY4_SIZE] = + { 1,1,0,1,1,1,0,0,0,0,1,0,1,0,0 }; + +static uint8 Bit5[POLY5_SIZE] = + { 0,0,1,0,1,1,0,0,1,1,1,1,1,0,0,0,1,1,0,1,1,1,0,1,0,1,0,0,0,0,1 }; + +/* I've treated the 'Div by 31' counter as another polynomial because of */ +/* the way it operates. It does not have a 50% duty cycle, but instead */ +/* has a 13:18 ratio (of course, 13+18 = 31). This could also be */ +/* implemented by using counters. */ + +static uint8 Div31[POLY5_SIZE] = + { 0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0 }; + +/* Rather than have a table with 511 entries, I use a random number */ +/* generator. */ + +static uint8 Bit9[POLY9_SIZE]; + +static uint8 P4[2]; /* Position pointer for the 4-bit POLY array */ +static uint8 P5[2]; /* Position pointer for the 5-bit POLY array */ +static uint16 P9[2]; /* Position pointer for the 9-bit POLY array */ + +static uint8 Div_n_cnt[2]; /* Divide by n counter. one for each channel */ +static uint8 Div_n_max[2]; /* Divide by n maximum, one for each channel */ + + +/* In my routines, I treat the sample output as another divide by N counter. */ +/* For better accuracy, the Samp_n_cnt has a fixed binary decimal point */ +/* which has 8 binary digits to the right of the decimal point. */ + +static uint16 Samp_n_max; /* Sample max, multiplied by 256 */ +static uint16 Samp_n_cnt; /* Sample cnt. */ + + + +/*****************************************************************************/ +/* Module: Tia_sound_init() */ +/* Purpose: to handle the power-up initialization functions */ +/* these functions should only be executed on a cold-restart */ +/* */ +/* Author: Ron Fries */ +/* Date: September 10, 1996 */ +/* */ +/* Inputs: sample_freq - the value for the '30 Khz' Tia audio clock */ +/* playback_freq - the playback frequency in samples per second */ +/* */ +/* Outputs: Adjusts local globals - no return value */ +/* */ +/*****************************************************************************/ + +void Tia_sound_init (uint16 sample_freq, uint16 playback_freq) +{ + uint8 chan; + int16 n; + + /* fill the 9bit polynomial with random bits */ + for (n=0; n 1) + { + /* decrement and loop */ + Div_n_cnt[chan]--; + } + /* otherwise if we've reached the bottom */ + else if (Div_n_cnt[chan] == 1) + { + /* reset the counter */ + Div_n_cnt[chan] = Div_n_max[chan]; + + /* the P5 counter has multiple uses, so we inc it here */ + P5[chan]++; + if (P5[chan] == POLY5_SIZE) + P5[chan] = 0; + + /* check clock modifier for clock tick */ + + /* if we're using pure tones OR + we're using DIV31 and the DIV31 bit is set OR + we're using POLY5 and the POLY5 bit is set */ + if (((AUDC[chan] & 0x02) == 0) || + (((AUDC[chan] & 0x01) == 0) && Div31[P5[chan]]) || + (((AUDC[chan] & 0x01) == 1) && Bit5[P5[chan]])) + { + if (AUDC[chan] & 0x04) /* pure modified clock selected */ + { + if (Outvol[chan]) /* if the output was set */ + Outvol[chan] = 0; /* turn it off */ + else + Outvol[chan] = AUDV[chan]; /* else turn it on */ + } + else if (AUDC[chan] & 0x08) /* check for p5/p9 */ + { + if (AUDC[chan] == POLY9) /* check for poly9 */ + { + /* inc the poly9 counter */ + P9[chan]++; + if (P9[chan] == POLY9_SIZE) + P9[chan] = 0; + + if (Bit9[P9[chan]]) /* if poly9 bit is set */ + Outvol[chan] = AUDV[chan]; + else + Outvol[chan] = 0; + } + else /* must be poly5 */ + { + if (Bit5[P5[chan]]) + Outvol[chan] = AUDV[chan]; + else + Outvol[chan] = 0; + } + } + else /* poly4 is the only remaining option */ + { + /* inc the poly4 counter */ + P4[chan]++; + if (P4[chan] == POLY4_SIZE) + P4[chan] = 0; + + if (Bit4[P4[chan]]) + Outvol[chan] = AUDV[chan]; + else + Outvol[chan] = 0; + } + } + } + } + + /* decrement the sample counter - value is 256 since the lower + byte contains the fractional part */ + Samp_n_cnt -= 256; + + /* if the count down has reached zero */ + if (Samp_n_cnt < 256) + { + /* adjust the sample counter */ + Samp_n_cnt += Samp_n_max; + + /* calculate the latest output value and place in buffer */ + *(buffer++) = Outvol[0] + Outvol[1]; + + /* and indicate one less byte to process */ + n--; + } + } +} + + +/*****************************************************************************/ +/* Module: Tia_process() */ +/* Purpose: To fill the output buffer with the sound output based on the */ +/* tia chip parameters. This routine has been optimized. */ +/* */ +/* Author: Ron Fries */ +/* Date: September 10, 1996 */ +/* */ +/* Inputs: *buffer - pointer to the buffer where the audio output will */ +/* be placed */ +/* n - size of the playback buffer */ +/* */ +/* Outputs: the buffer will be filled with n bytes of audio - no return val */ +/* */ +/*****************************************************************************/ + +void Tia_process (register unsigned char *buffer, register uint16 n) +{ + register uint8 audc0,audv0,audc1,audv1; + register uint8 div_n_cnt0,div_n_cnt1; + register uint8 p5_0, p5_1,outvol_0,outvol_1; + + audc0 = AUDC[0]; + audv0 = AUDV[0]; + audc1 = AUDC[1]; + audv1 = AUDV[1]; + + /* make temporary local copy */ + p5_0 = P5[0]; + p5_1 = P5[1]; + outvol_0 = Outvol[0]; + outvol_1 = Outvol[1]; + div_n_cnt0 = Div_n_cnt[0]; + div_n_cnt1 = Div_n_cnt[1]; + + /* loop until the buffer is filled */ + while (n) + { + /* Process channel 0 */ + if (div_n_cnt0 > 1) + { + div_n_cnt0--; + } + else if (div_n_cnt0 == 1) + { + div_n_cnt0 = Div_n_max[0]; + + /* the P5 counter has multiple uses, so we inc it here */ + p5_0++; + if (p5_0 == POLY5_SIZE) + p5_0 = 0; + + /* check clock modifier for clock tick */ + if (((audc0 & 0x02) == 0) || + (((audc0 & 0x01) == 0) && Div31[p5_0]) || + (((audc0 & 0x01) == 1) && Bit5[p5_0])) + { + if (audc0 & 0x04) /* pure modified clock selected */ + { + if (outvol_0) /* if the output was set */ + outvol_0 = 0; /* turn it off */ + else + outvol_0 = audv0; /* else turn it on */ + } + else if (audc0 & 0x08) /* check for p5/p9 */ + { + if (audc0 == POLY9) /* check for poly9 */ + { + /* inc the poly9 counter */ + P9[0]++; + if (P9[0] == POLY9_SIZE) + P9[0] = 0; + + if (Bit9[P9[0]]) + outvol_0 = audv0; + else + outvol_0 = 0; + } + else /* must be poly5 */ + { + if (Bit5[p5_0]) + outvol_0 = audv0; + else + outvol_0 = 0; + } + } + else /* poly4 is the only remaining option */ + { + /* inc the poly4 counter */ + P4[0]++; + if (P4[0] == POLY4_SIZE) + P4[0] = 0; + + if (Bit4[P4[0]]) + outvol_0 = audv0; + else + outvol_0 = 0; + } + } + } + + + /* Process channel 1 */ + if (div_n_cnt1 > 1) + { + div_n_cnt1--; + } + else if (div_n_cnt1 == 1) + { + div_n_cnt1 = Div_n_max[1]; + + /* the P5 counter has multiple uses, so we inc it here */ + p5_1++; + if (p5_1 == POLY5_SIZE) + p5_1 = 0; + + /* check clock modifier for clock tick */ + if (((audc1 & 0x02) == 0) || + (((audc1 & 0x01) == 0) && Div31[p5_1]) || + (((audc1 & 0x01) == 1) && Bit5[p5_1])) + { + if (audc1 & 0x04) /* pure modified clock selected */ + { + if (outvol_1) /* if the output was set */ + outvol_1 = 0; /* turn it off */ + else + outvol_1 = audv1; /* else turn it on */ + } + else if (audc1 & 0x08) /* check for p5/p9 */ + { + if (audc1 == POLY9) /* check for poly9 */ + { + /* inc the poly9 counter */ + P9[1]++; + if (P9[1] == POLY9_SIZE) + P9[1] = 0; + + if (Bit9[P9[1]]) + outvol_1 = audv1; + else + outvol_1 = 0; + } + else /* must be poly5 */ + { + if (Bit5[p5_1]) + outvol_1 = audv1; + else + outvol_1 = 0; + } + } + else /* poly4 is the only remaining option */ + { + /* inc the poly4 counter */ + P4[1]++; + if (P4[1] == POLY4_SIZE) + P4[1] = 0; + + if (Bit4[P4[1]]) + outvol_1 = audv1; + else + outvol_1 = 0; + } + } + } + + /* decrement the sample counter - value is 256 since the lower + byte contains the fractional part */ + Samp_n_cnt -= 256; + + /* if the count down has reached zero */ + if (Samp_n_cnt < 256) + { + /* adjust the sample counter */ + Samp_n_cnt += Samp_n_max; + + /* calculate the latest output value and place in buffer */ + *(buffer++) = outvol_0 + outvol_1; + + /* and indicate one less byte to process */ + n--; + } + } + + /* save for next round */ + P5[0] = p5_0; + P5[1] = p5_1; + Outvol[0] = outvol_0; + Outvol[1] = outvol_1; + Div_n_cnt[0] = div_n_cnt0; + Div_n_cnt[1] = div_n_cnt1; + +} + + diff --git a/stella/src/ui/sound/TIASound.h b/stella/src/ui/sound/TIASound.h new file mode 100644 index 000000000..5c0b6c371 --- /dev/null +++ b/stella/src/ui/sound/TIASound.h @@ -0,0 +1,54 @@ +/*****************************************************************************/ +/* */ +/* Module: TIA Chip Sound Simulator Includes, V1.1 */ +/* Purpose: Define global function prototypes and structures for the TIA */ +/* Chip Sound Simulator. */ +/* Author: Ron Fries */ +/* */ +/* Revision History: */ +/* 10-Sep-96 - V1.0 - Initial Release */ +/* 14-Jan-97 - V1.1 - Added compiler directives to facilitate compilation */ +/* on a C++ compiler. */ +/* */ +/*****************************************************************************/ +/* */ +/* License Information and Copyright Notice */ +/* ======================================== */ +/* */ +/* TiaSound is Copyright(c) 1997 by Ron Fries */ +/* */ +/* This library is free software; you can redistribute it and/or modify it */ +/* under the terms of version 2 of the GNU Library General Public License */ +/* as published by the Free Software Foundation. */ +/* */ +/* This library is distributed in the hope that it will be useful, but */ +/* WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library */ +/* General Public License for more details. */ +/* To obtain a copy of the GNU Library General Public License, write to the */ +/* Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ +/* */ +/* Any permitted reproduction of these routines, in whole or in part, must */ +/* bear this legend. */ +/* */ +/*****************************************************************************/ + +#ifndef _TIASOUND_H +#define _TIASOUND_H + +#ifdef __cplusplus +extern "C" { +#endif + +void Tia_sound_init (unsigned int sample_freq, unsigned int playback_freq); +void Update_tia_sound (unsigned int addr, unsigned char val); +void Tia_process_2 (register unsigned char *buffer, + register unsigned int n); +void Tia_process (register unsigned char *buffer, + register unsigned int n); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/stella/src/ui/sound/TIASound.txt b/stella/src/ui/sound/TIASound.txt new file mode 100644 index 000000000..2819ef9ed --- /dev/null +++ b/stella/src/ui/sound/TIASound.txt @@ -0,0 +1,297 @@ + TIASOUND.C - TIA SOUND EMULATION V1.0 + ===================================== + +As I'm sure you've already discovered, the Stella manual isn't always +correct. My observations show what I believe to be several discrepancies +in the description of the distortions. Of course, I could be wrong on a few +of these, so if some of the games don't sound right, please let me know +which ones. If possible, it would be best if you could send me a wave file +of what it is supposed to sound like, preferably at 44 KHz. Only a few +seconds should be necessary. + + +TIA AUDIO CIRCUITRY +=================== + + +THE HARDWARE +------------ + +In general, the sound hardware can be described as follows: + + +Selecting the Clock Modifier - Bits D0 and D1 +--------------------------------------------- + +Bits D0 and D1 select the output clock modifier: + + D1 D0 + ------- + 0 0 - direct clock (pure) + 0 1 - direct clock (pure) + 1 0 - divide by 31 + 1 1 - 5-bit polynomial + +The 'divide by 31' isn't simply the input clock divided by 31. It is, in +essence, a 5-bit polynomial with only two bits set. The resulting square +wave actually has a 13:18 ratio. This may be implemented in the hardware +as a pair of traps on a 5-bit counter. + + +Selecting the Source Pattern - Bits D2 and D3 +--------------------------------------------- + +Bits D2 and D3 select the source to be clocked: + + D3 D2 + ------- + 0 0 - 4-bit polynomial + 0 1 - pure (Q') + 1 0 - 5-bit polynomial + 1 1 - pure (Q') + +The 'pure' tones are generated by toggling the output. Whenever a clock +tick is received, the output will change states. I've used the notation Q' +to indicate the 'logical NOT of the last output'. Note that since the output +toggles, this can be thought of as a divide by 2 since the output frequency +will be half of the input frequency. This is only true for the pure tones. + + +Selecting the Source Clock - Bits D2 and D3 +------------------------------------------- + +When bits D2 and D3 are both set, it affects the source clock. I believe +the '30KHz' clock is actually the 3.58MHz clock divided by 114. When bits +D2 and D3 are set, the input source is switched to the 1.19MHz clock, so the +'30KHz' source clock is reduced to approximately 10KHz. + + +Exceptions - Selecting No Output or the 9-bit Polynomial +-------------------------------------------------------- + +There are two exceptions that occur when bits D0-D2 are all 0. If AUDC is +zero (0000), then I believe the output is set equal to the volume. If AUDC is +equal to 8 (1000), the 9-bit polynomial is selected as the source to be +clocked. + + +Updated Detailed Functions for AUDC +----------------------------------- + +From my observations, I would describe the distortion selections as follows: + + HEX D3 D2 D1 D0 Clock Source Clock Modifier Source Pattern + --- ------------- -------------- ---------------- ---------------- + 0 0 0 0 0 3.58 MHz/114 -> none (pure) -> none + 1 0 0 0 1 3.58 MHz/114 -> none (pure) -> 4-bit poly + 2 0 0 1 0 3.58 MHz/114 -> divide by 31 -> 4-bit poly + 3 0 0 1 1 3.58 MHz/114 -> 5-bit poly -> 4-bit poly + 4 0 1 0 0 3.58 MHz/114 -> none (pure) -> pure (~Q) + 5 0 1 0 1 3.58 MHz/114 -> none (pure) -> pure (~Q) + 6 0 1 1 0 3.58 MHz/114 -> divide by 31 -> pure (~Q) + 7 0 1 1 1 3.58 MHz/114 -> 5-bit poly -> pure (~Q) + 8 1 0 0 0 3.58 MHz/114 -> none (pure) -> 9-bit poly + 9 1 0 0 1 3.58 MHz/114 -> none (pure) -> 5-bit poly + A 1 0 1 0 3.58 MHz/114 -> divide by 31 -> 5-bit poly + B 1 0 1 1 3.58 MHz/114 -> 5-bit poly -> 5-bit poly + C 1 1 0 0 1.19 MHz/114 -> none (pure) -> pure (~Q) + D 1 1 0 1 1.19 MHz/114 -> none (pure) -> pure (~Q) + E 1 1 1 0 1.19 MHz/114 -> divide by 31 -> pure (~Q) + F 1 1 1 1 1.19 MHz/114 -> 5-bit poly -> pure (~Q) + +For the most part, this follows the Stella manual, but there are a few +differences. Probably the most notable are hex entries 'A' and 'B', which +are listed in the Stella manual as 'div 31: pure tone' and 'set last 4 bits +to 1'. + +On entries 'A' and 'B', both the data source and the clock have the same +number of entries (31). Because of this, they will always align in the +same way. Entry 'A' will then reduce to a pure 'div by 31' output which +is identical to entry '6'. On 'B', both the source and the clock align +in such a way that the output will always be 1. + + + +THE POLYNOMIALS +--------------- + +The 4-bit, 5-bit, and 9-bit polynomials are essentially tables containing +a random series of bits (they are implemented in hardware as shift +registers). Because the tables are fixed in length, the 'random' pattern +will repeat periodically. + +The size of the table is described by its name. The actual size of the +table is 2^x - 1, where x is either the 4, 5 or 9. The 4-bit polynomial +has 15 entries, the 5-bit polynomial has 31 entries, and the 9-bit +polynomial has 511 entries. + +I've performed some analysis on the output of the actual Atari 2600, and +believe I have the actual 4-bit and 5-bit polynomials used by the Atari. +These have been encoded as byte arrays in my routines. For the 9-bit +polynomial, I use a random number generator which should produce +approximately the same results. + + +THE CLOCK +--------- + +I believe the input clock for the audio is a division of the main system +clock. With this assumption, I determined that the input clock for the +audio is equal to the 3.58MHz system clock divided by 114. Note that this +produces an actual audio clock of 31.4 KHz. This value closely matches the +frequencies I recorded from my unit. The Stella manual describes the Audio +Clock as approximately 30KHz, which I suppose is correct if you consider 5% +to be approximate. + +If both bits D2 and D3 of the AUDC register are set, I believe the TIA chip +uses the 1.19MHz clock for the base audio clock instead of the 3.58MHz. +This, of course, produces the 'divide by 3' functionality described in the +Stella manual. + + + +TIASOUND.C +========== + +The TIASOUND.C file is the heart of the TIA Sound Emulation program. +Although the routines in the file must work together, no other files are +modules are required for operation. A header file, 'TIASOUND.H', has +been included for use in other modules, and provides the necessary +function prototypes. I've attempted to make the routines as portable as +possible, so the file should compile on almost any compiler with little +or no modification. + +I have made some attempts at optimizing the routines, though I am sure +more optimization can be done. They are currently only available in 'C'. +I'll be happy to convert them to assembly language if desired. Please feel +free to send me e-mail (see below). + +The routines are easy to use. Detailed descriptions on the function calls +are listed below. + + +GENERAL OVERVIEW +---------------- + +On start-up of the system, a single call should be made to Tia_sound_init. +This routine will prepare the structures for sound output. This routine +can be called again if necessary during warm-start or other reset. + +Once in the main loop, there are two other functions that will be used. +Whenever the system needs to write to either the AUDC, AUDV or AUDF values, +a call should be made to the Update_tia_sound routine. This routine will +take care of updating the internal registers. It will pre-calculate several +values to help with optimization. + +The only other routine that is called is the Tia_process function. This +function will fill a audio buffer with a specified number of bytes. This +function should be called whenever a new audio buffer is required. + +For best results, I recommend using at least two output buffers. Using this +scheme, the sound card can be playing one buffer while the system is filling +the other. + + +DETAILED FUNCTION DESCRIPTIONS +------------------------------ + +Tia_sound_init(uint16 sample_freq, uint16 playback_freq) +-------------------------------------------------------- + +This function initializes the structures used by the TiaSound.C routines. +This function takes two parameters: the sample frequency and the playback +frequency. The sample frequency is the frequency of the 30KHz source clock. +For my calculations, this clock is about 31.4 KHz, though any 16-bit +unsigned integer value can be used (1-65535). + +The playback frequency is the frequency of the sound playback (the frequency +used by the sound card). For best results, both the playback frequency and +the sample frequency should be identical. In the case where the sound card +cannot support 31.4 KHz (i.e. the original SB and many 'SB compatible' cards), +there are three options: + +1) Set the sample frequency to 31400 and the playback frequency to the + maximum possible (e.g. 22050). Though the system will reproduce all + output values, a significant amount of aliasing is introduced. + +2) Set the sample frequency and the playback frequency to the maximum possible + (e.g. 22050). In this case, all output values are reproduced with no + distortions; however, the pitch is about 2/3 of an octave low. + +3) Set the sample frequency to 31400 and the playback frequency to exactly + 1/2 of the sample frequency (15700). In this case, the output frequency + is correct and the aliasing is minimized. The aliasing is only + noticeable on the higher frequencies. Most notably, the highest pure tone + (AUDF = 0) is inaudible. + +Feel free to experiment to find other alternatives as well. + +This function has no return value (void). + + +Update_tia_sound (uint16 addr, uint8 val) +----------------------------------------- + +This function should be called each time an AUDC, AUDF or AUDV value +changes. This function takes two parameters: the address to change and +the new value. The address should be one of the following values: + + Addr Description + ---- ----------- + 0x15 AUDC0 + 0x16 AUDC1 + 0x17 AUDF0 + 0x18 AUDF1 + 0x19 AUDV0 + 0x1A AUDV1 + +Any values outside of this range will be ignored. + +The routine pre-calculates several values that are needed by the +processing function. This is done to optimize performance. + +This function has no return value (void). + + +Tia_process (unsigned char *buffer, uint16 n) +--------------------------------------------- + +This function calculates and fills a buffer with unsigned 8-bit mono audio. +This function takes two parameters: a pointer to the buffer to fill and +the size of the buffer (limited to 65535). This function fills the +buffer based on the requested size and returns. It automatically +updates the pointers for the next call, so subsequent calls to this function +will provide a continuous stream of data. + +The size of the buffer that is needed depends on the playback frequency. +It is best to keep the buffer as small as possible to maximize response time +to changes in the sound. Of course, the minimum size is dependent on +system and emulator performance. + +Selecting the correct buffer size is a careful balance. Selecting a buffer +size that is too small will produce noticeable clicks in the output, though +selecting a size that is too large will cause a poor response time and +possible delays in the system when the new buffer is filled. + +This function has no return value (void). + + +License Information and Copyright Notice +======================================== + +TiaSound is Copyright(c) 1996 by Ron Fries + +This library is free software; you can redistribute it and/or modify it under +the terms of version 2 of the GNU Library General Public License as published +by the Free Software Foundation. + +This library is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS +FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more +details. + +To obtain a copy of the GNU Library General Public License, write to the Free +Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +Any permitted reproduction of these routines, in whole or in part, must bear +this legend. diff --git a/stella/src/ui/sound/makefile b/stella/src/ui/sound/makefile new file mode 100644 index 000000000..8b6f5063e --- /dev/null +++ b/stella/src/ui/sound/makefile @@ -0,0 +1,36 @@ +############################################################################### +## C compiler to use +############################################################################### +CC = gcc + +CFLAGS = -DWIN32 -O -ansi -Wall + +OBJS = TIASound.o + +all: + @echo "" + @echo "To build stella-sound, type: 'make '" + @echo "" + @echo "where is one of:" + @echo "" + @echo " linux Linux (same as uss)" + @echo " uss Systems with the Unix Sound System (same as oss)" + @echo " oss systems with the Open Sound System" + @echo " bsdi BSD/OS 4.0" + @echo "" + @echo "Hopefully new versions will be added soon!" + @echo "" + +bsdi: + make oss CFLAGS="-DWIN32 -O3 -Wall" + +linux: oss + +uss: oss + +oss: $(OBJS) OSS.o + $(CC) -o stella-sound OSS.o $(OBJS) + +clean: + rm -f *.o stella-sound + diff --git a/stella/src/ui/x11/mainX11.cxx b/stella/src/ui/x11/mainX11.cxx new file mode 100644 index 000000000..5f31066f6 --- /dev/null +++ b/stella/src/ui/x11/mainX11.cxx @@ -0,0 +1,912 @@ +//============================================================================ +// +// SSSS tt lll lll +// SS SS tt ll ll +// SS tttttt eeee ll ll aaaa +// SSSS tt ee ee ll ll aa +// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator" +// SS SS tt ee ll ll aa aa +// SSSS ttt eeeee llll llll aaaaa +// +// Copyright (c) 1995-1999 by Bradford W. Mott +// +// See the file "license" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +// +// $Id: mainX11.cxx,v 1.1.1.1 2001-12-27 19:54:36 bwmott Exp $ +//============================================================================ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + +#include "bspf.hxx" +#include "Console.hxx" +#include "DefProps.hxx" +#include "Event.hxx" +#include "MediaSrc.hxx" +#include "PropsSet.hxx" +#include "SndUnix.hxx" +#include "System.hxx" + +#ifdef LINUX_JOYSTICK + #include + #include + #include + + // File descriptors for the joystick devices + int theLeftJoystickFd; + int theRightJoystickFd; +#endif + +// Globals for X windows stuff +Display* theDisplay; +string theDisplayName = ""; +int theScreen; +Visual* theVisual; +Window theWindow; +Colormap thePrivateColormap; +bool theUsePrivateColormapFlag = false; + +// A graphic context for each of the 2600's colors +GC theGCTable[256]; + +// Enumeration of the possible window sizes +enum WindowSize { Small = 1, Medium = 2, Large = 3 }; + +// Indicates the current size of the window +WindowSize theWindowSize = Medium; + +// Indicates the width and height of the game display based on properties +uInt32 theHeight; +uInt32 theWidth; + +// Pointer to the console object or the null pointer +Console* theConsole; + +// Event object to use +Event theEvent; + +// Indicates if the entire frame should be redrawn +bool theRedrawEntireFrameFlag = true; + +// Indicates if the user wants to quit +bool theQuitIndicator = false; + +// Indicates what the desired frame rate is +uInt32 theDesiredFrameRate = 60; + +// Indicate which paddle mode we're using: +// 0 - Mouse emulates paddle 0 +// 1 - Mouse emulates paddle 1 +// 2 - Mouse emulates paddle 2 +// 3 - Mouse emulates paddle 3 +// 4 - Use real Atari 2600 paddles +uInt32 thePaddleMode = 0; + +/** + This routine should be called once the console is created to setup + the X11 connection and open a window for us to use +*/ +void setupX11() +{ + // Get the desired width and height of the display + theHeight = theConsole->mediaSource().height(); + theWidth = theConsole->mediaSource().width(); + + // Figure out the desired size of the window + int width = theWidth; + int height = theHeight; + if(theWindowSize == Small) + { + width *= 2; + } + else if(theWindowSize == Medium) + { + width *= 4; + height *= 2; + } + else + { + width *= 6; + height *= 3; + } + + // Open a connection to the X server + if(theDisplayName == "") + theDisplay = XOpenDisplay(NULL); + else + theDisplay = XOpenDisplay(theDisplayName.c_str()); + + // Verify that the connection was made + if(theDisplay == NULL) + { + cerr << "ERROR: Cannot open X Windows display...\n"; + exit(1); + } + + theScreen = DefaultScreen(theDisplay); + theVisual = DefaultVisual(theDisplay, theScreen); + Window rootWindow = RootWindow(theDisplay, theScreen); + + theWindow = XCreateSimpleWindow(theDisplay, rootWindow, 0, 0, + width, height, CopyFromParent, CopyFromParent, + BlackPixel(theDisplay, theScreen)); + + // If requested create a private colormap for the window + if(theUsePrivateColormapFlag) + { + thePrivateColormap = XCreateColormap(theDisplay, theWindow, + theVisual, AllocNone); + } + + XSizeHints hints; + hints.flags = PSize | PMinSize | PMaxSize; + hints.min_width = hints.max_width = hints.width = width; + hints.min_height = hints.max_height = hints.height = height; + + // Set window and icon name, size hints and other properties + char name[512]; + sprintf(name, "Stella: \"%s\"", + theConsole->properties().get("Cartridge.Name").c_str()); + + XSetStandardProperties(theDisplay, theWindow, name, name, None, 0, 0, &hints); + + // Allocate colors in the default colormap + const uInt32* palette = theConsole->mediaSource().palette(); + for(uInt32 t = 0; t < 256; t += 2) + { + XColor color; + + color.red = (palette[t] & 0x00ff0000) >> 8 ; + color.green = (palette[t] & 0x0000ff00) ; + color.blue = (palette[t] & 0x000000ff) << 8; + color.flags = DoRed | DoGreen | DoBlue; + + if(theUsePrivateColormapFlag) + XAllocColor(theDisplay, thePrivateColormap, &color); + else + XAllocColor(theDisplay, DefaultColormap(theDisplay, theScreen), &color); + + XGCValues values; + values.foreground = color.pixel; + theGCTable[t] = XCreateGC(theDisplay, theWindow, GCForeground, &values); + theGCTable[t + 1] = theGCTable[t]; + } + + // If requested install a private colormap for the window + if(theUsePrivateColormapFlag) + { + XSetWindowColormap(theDisplay, theWindow, thePrivateColormap); + } + + XSelectInput(theDisplay, theWindow, ExposureMask); + XMapWindow(theDisplay, theWindow); + + XEvent event; + do + { + XNextEvent(theDisplay, &event); + } while (event.type != Expose); + + uInt32 mask = ExposureMask | KeyPressMask | KeyReleaseMask; + + // If we're using the mouse for paddle emulation then enable mouse events + if(((theConsole->properties().get("Controller.Left") == "Paddles") || + (theConsole->properties().get("Controller.Right") == "Paddles")) + && (thePaddleMode != 4)) + { + mask |= (PointerMotionMask | ButtonPressMask | ButtonReleaseMask); + } + + XSelectInput(theDisplay, theWindow, mask); +} + +/** + This routine should be called anytime the display needs to be updated +*/ +void updateDisplay(MediaSource& mediaSource) +{ + uInt8* currentFrame = mediaSource.currentFrameBuffer(); + uInt8* previousFrame = mediaSource.previousFrameBuffer(); + uInt16 screenMultiple = (uInt16)theWindowSize; + + struct Rectangle + { + uInt8 color; + uInt16 x, y, width, height; + } rectangles[2][160]; + + // This array represents the rectangles that need displaying + // on the current scanline we're processing + Rectangle* currentRectangles = rectangles[0]; + + // This array represents the rectangles that are still active + // from the previous scanlines we have processed + Rectangle* activeRectangles = rectangles[1]; + + // Indicates the number of active rectangles + uInt16 activeCount = 0; + + + // This update procedure requires theWidth to be a multiple of four. + // This is validated when the properties are loaded. + for(uInt16 y = 0; y < theHeight; ++y) + { + // Indicates the number of current rectangles + uInt16 currentCount = 0; + + // Look at four pixels at a time to see if anything has changed + uInt32* current = (uInt32*)(currentFrame); + uInt32* previous = (uInt32*)(previousFrame); + + for(uInt16 x = 0; x < theWidth; x += 4, ++current, ++previous) + { + // Has something changed in this set of four pixels? + if((*current != *previous) || theRedrawEntireFrameFlag) + { + uInt8* c = (uInt8*)current; + uInt8* p = (uInt8*)previous; + + // Look at each of the bytes that make up the uInt32 + for(uInt16 i = 0; i < 4; ++i, ++c, ++p) + { + // See if this pixel has changed + if((*c != *p) || theRedrawEntireFrameFlag) + { + // Can we extend a rectangle or do we have to create a new one? + if((currentCount != 0) && + (currentRectangles[currentCount - 1].color == *c) && + ((currentRectangles[currentCount - 1].x + + currentRectangles[currentCount - 1].width) == (x + i))) + { + currentRectangles[currentCount - 1].width += 1; + } + else + { + currentRectangles[currentCount].x = x + i; + currentRectangles[currentCount].y = y; + currentRectangles[currentCount].width = 1; + currentRectangles[currentCount].height = 1; + currentRectangles[currentCount].color = *c; + currentCount++; + } + } + } + } + } + + // Merge the active and current rectangles flushing any that are of no use + uInt16 activeIndex = 0; + + for(uInt16 t = 0; (t < currentCount) && (activeIndex < activeCount); ++t) + { + Rectangle& current = currentRectangles[t]; + Rectangle& active = activeRectangles[activeIndex]; + + // Can we merge the current rectangle with an active one? + if((current.x == active.x) && (current.width == active.width) && + (current.color == active.color)) + { + current.y = active.y; + current.height = active.height + 1; + + ++activeIndex; + } + // Is it impossible for this active rectangle to be merged? + else if(current.x >= active.x) + { + // Flush the active rectangle + XFillRectangle(theDisplay, theWindow, theGCTable[active.color], + active.x * 2 * screenMultiple, active.y * screenMultiple, + active.width * 2 * screenMultiple, active.height * screenMultiple); + + ++activeIndex; + } + } + + // Flush any remaining active rectangles + for(uInt16 s = activeIndex; s < activeCount; ++s) + { + Rectangle& active = activeRectangles[s]; + + XFillRectangle(theDisplay, theWindow, theGCTable[active.color], + active.x * 2 * screenMultiple, active.y * screenMultiple, + active.width * 2 * screenMultiple, active.height * screenMultiple); + } + + // We can now make the current rectangles into the active rectangles + Rectangle* tmp = currentRectangles; + currentRectangles = activeRectangles; + activeRectangles = tmp; + activeCount = currentCount; + + currentFrame += theWidth; + previousFrame += theWidth; + } + + // Flush any rectangles that are still active + for(uInt16 t = 0; t < activeCount; ++t) + { + Rectangle& active = activeRectangles[t]; + + XFillRectangle(theDisplay, theWindow, theGCTable[active.color], + active.x * 2 * screenMultiple, active.y * screenMultiple, + active.width * 2 * screenMultiple, active.height * screenMultiple); + } + + // The frame doesn't need to be completely redrawn anymore + theRedrawEntireFrameFlag = false; +} + +/** + This routine should be called regularly to handle events +*/ +void handleEvents() +{ + struct Switches + { + KeySym scanCode; + Event::Type eventCode; + }; + + static Switches list[] = { + { XK_1, Event::KeyboardZero1 }, + { XK_2, Event::KeyboardZero2 }, + { XK_3, Event::KeyboardZero3 }, + { XK_q, Event::KeyboardZero4 }, + { XK_w, Event::KeyboardZero5 }, + { XK_e, Event::KeyboardZero6 }, + { XK_a, Event::KeyboardZero7 }, + { XK_s, Event::KeyboardZero8 }, + { XK_d, Event::KeyboardZero9 }, + { XK_z, Event::KeyboardZeroStar }, + { XK_x, Event::KeyboardZero0 }, + { XK_c, Event::KeyboardZeroPound }, + + { XK_8, Event::KeyboardOne1 }, + { XK_9, Event::KeyboardOne2 }, + { XK_0, Event::KeyboardOne3 }, + { XK_i, Event::KeyboardOne4 }, + { XK_o, Event::KeyboardOne5 }, + { XK_p, Event::KeyboardOne6 }, + { XK_k, Event::KeyboardOne7 }, + { XK_l, Event::KeyboardOne8 }, + { XK_semicolon, Event::KeyboardOne9 }, + { XK_comma, Event::KeyboardOneStar }, + { XK_period, Event::KeyboardOne0 }, + { XK_slash, Event::KeyboardOnePound }, + + { XK_Down, Event::JoystickZeroDown }, + { XK_Up, Event::JoystickZeroUp }, + { XK_Left, Event::JoystickZeroLeft }, + { XK_Right, Event::JoystickZeroRight }, + { XK_space, Event::JoystickZeroFire }, + { XK_Return, Event::JoystickZeroFire }, + { XK_z, Event::BoosterGripZeroTrigger }, + { XK_x, Event::BoosterGripZeroBooster }, + + { XK_w, Event::JoystickZeroUp }, + { XK_s, Event::JoystickZeroDown }, + { XK_a, Event::JoystickZeroLeft }, + { XK_d, Event::JoystickZeroRight }, + { XK_Tab, Event::JoystickZeroFire }, + { XK_1, Event::BoosterGripZeroTrigger }, + { XK_2, Event::BoosterGripZeroBooster }, + + { XK_l, Event::JoystickOneDown }, + { XK_o, Event::JoystickOneUp }, + { XK_k, Event::JoystickOneLeft }, + { XK_semicolon, Event::JoystickOneRight }, + { XK_j, Event::JoystickOneFire }, + { XK_n, Event::BoosterGripOneTrigger }, + { XK_m, Event::BoosterGripOneBooster }, + + { XK_F1, Event::ConsoleSelect }, + { XK_F2, Event::ConsoleReset }, + { XK_F3, Event::ConsoleColor }, + { XK_F4, Event::ConsoleBlackWhite }, + { XK_F5, Event::ConsoleLeftDifficultyA }, + { XK_F6, Event::ConsoleLeftDifficultyB }, + { XK_F7, Event::ConsoleRightDifficultyA }, + { XK_F8, Event::ConsoleRightDifficultyB } + }; + + static Event keyboardEvent; + + XEvent event; + + while(XCheckWindowEvent(theDisplay, theWindow, + ExposureMask | KeyPressMask | KeyReleaseMask | ButtonPressMask | + ButtonReleaseMask | PointerMotionMask, &event)) + { + char buffer[20]; + KeySym key; + XComposeStatus compose; + + if((event.type == KeyPress) || (event.type == KeyRelease)) + { + XLookupString(&event.xkey, buffer, 20, &key, &compose); + if((key == XK_Escape) && (event.type == KeyPress)) + { + theQuitIndicator = true; + } + else if((key == XK_equal) && (event.type == KeyPress)) + { + if(theWindowSize == Small) + theWindowSize = Medium; + else if(theWindowSize == Medium) + theWindowSize = Large; + else + theWindowSize = Small; + + // Figure out the desired size of the window + int width = theWidth; + int height = theHeight; + if(theWindowSize == Small) + { + width *= 2; + } + else if(theWindowSize == Medium) + { + width *= 4; + height *= 2; + } + else + { + width *= 6; + height *= 3; + } + + XSizeHints hints; + hints.flags = PSize | PMinSize | PMaxSize; + hints.min_width = hints.max_width = hints.width = width; + hints.min_height = hints.max_height = hints.height = height; + XSetStandardProperties(theDisplay, theWindow, 0, 0, None, 0, 0, &hints); + XResizeWindow(theDisplay, theWindow, width, height); + + theRedrawEntireFrameFlag = true; + } + else + { + for(unsigned int i = 0; i < sizeof(list) / sizeof(Switches); ++i) + { + if(list[i].scanCode == key) + { + theEvent.set(list[i].eventCode, + (event.type == KeyPress) ? 1 : 0); + keyboardEvent.set(list[i].eventCode, + (event.type == KeyPress) ? 1 : 0); + } + } + } + } + else if(event.type == MotionNotify) + { + Int32 resistance = 0; + + if(theWindowSize == Small) + { + int x = (theWidth * 2) - event.xmotion.x; + resistance = (Int32)((1000000.0 * x) / (theWidth * 2)); + } + else if(theWindowSize == Medium) + { + int x = (theWidth * 4) - event.xmotion.x; + resistance = (Int32)((1000000.0 * x) / (theWidth * 4)); + } + else + { + int x = (theWidth * 6) - event.xmotion.x; + resistance = (Int32)((1000000.0 * x) / (theWidth * 6)); + } + + // Now, set the event of the correct paddle to the calculated resistance + if(thePaddleMode == 0) + theEvent.set(Event::PaddleZeroResistance, resistance); + else if(thePaddleMode == 1) + theEvent.set(Event::PaddleOneResistance, resistance); + else if(thePaddleMode == 2) + theEvent.set(Event::PaddleTwoResistance, resistance); + else if(thePaddleMode == 3) + theEvent.set(Event::PaddleThreeResistance, resistance); + } + else if(event.type == ButtonPress) + { + if(thePaddleMode == 0) + theEvent.set(Event::PaddleZeroFire, 1); + else if(thePaddleMode == 1) + theEvent.set(Event::PaddleOneFire, 1); + else if(thePaddleMode == 2) + theEvent.set(Event::PaddleTwoFire, 1); + else if(thePaddleMode == 3) + theEvent.set(Event::PaddleThreeFire, 1); + } + else if(event.type == ButtonRelease) + { + if(thePaddleMode == 0) + theEvent.set(Event::PaddleZeroFire, 0); + else if(thePaddleMode == 1) + theEvent.set(Event::PaddleOneFire, 0); + else if(thePaddleMode == 2) + theEvent.set(Event::PaddleTwoFire, 0); + else if(thePaddleMode == 3) + theEvent.set(Event::PaddleThreeFire, 0); + } + else if(event.type == Expose) + { + theRedrawEntireFrameFlag = true; + } + } + +#ifdef LINUX_JOYSTICK + // Read joystick events and modify event states + if(theLeftJoystickFd >= 0) + { + struct js_event event; + + // Process each joystick event that's queued-up + while(read(theLeftJoystickFd, &event, sizeof(struct js_event)) > 0) + { + if((event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) + { + if(event.number == 0) + { + theEvent.set(Event::JoystickZeroFire, event.value ? + 1 : keyboardEvent.get(Event::JoystickZeroFire)); + + // If we're using real paddles then set paddle event as well + if(thePaddleMode == 4) + theEvent.set(Event::PaddleZeroFire, event.value); + } + else if(event.number == 1) + { + theEvent.set(Event::BoosterGripZeroTrigger, event.value ? + 1 : keyboardEvent.get(Event::BoosterGripZeroTrigger)); + + // If we're using real paddles then set paddle event as well + if(thePaddleMode == 4) + theEvent.set(Event::PaddleOneFire, event.value); + } + } + else if((event.type & ~JS_EVENT_INIT) == JS_EVENT_AXIS) + { + if(event.number == 0) + { + theEvent.set(Event::JoystickZeroLeft, (event.value < -16384) ? + 1 : keyboardEvent.get(Event::JoystickZeroLeft)); + theEvent.set(Event::JoystickZeroRight, (event.value > 16384) ? + 1 : keyboardEvent.get(Event::JoystickZeroRight)); + + // If we're using real paddles then set paddle events as well + if(thePaddleMode == 4) + { + uInt32 r = (uInt32)((1.0E6L * (event.value + 32767L)) / 65536); + theEvent.set(Event::PaddleZeroResistance, r); + } + } + else if(event.number == 1) + { + theEvent.set(Event::JoystickZeroUp, (event.value < -16384) ? + 1 : keyboardEvent.get(Event::JoystickZeroUp)); + theEvent.set(Event::JoystickZeroDown, (event.value > 16384) ? + 1 : keyboardEvent.get(Event::JoystickZeroDown)); + + // If we're using real paddles then set paddle events as well + if(thePaddleMode == 4) + { + uInt32 r = (uInt32)((1.0E6L * (event.value + 32767L)) / 65536); + theEvent.set(Event::PaddleOneResistance, r); + } + } + } + } + } + + if(theRightJoystickFd >= 0) + { + struct js_event event; + + // Process each joystick event that's queued-up + while(read(theRightJoystickFd, &event, sizeof(struct js_event)) > 0) + { + if((event.type & ~JS_EVENT_INIT) == JS_EVENT_BUTTON) + { + if(event.number == 0) + { + theEvent.set(Event::JoystickOneFire, event.value ? + 1 : keyboardEvent.get(Event::JoystickOneFire)); + + // If we're using real paddles then set paddle event as well + if(thePaddleMode == 4) + theEvent.set(Event::PaddleTwoFire, event.value); + } + else if(event.number == 1) + { + theEvent.set(Event::BoosterGripOneTrigger, event.value ? + 1 : keyboardEvent.get(Event::BoosterGripOneTrigger)); + + // If we're using real paddles then set paddle event as well + if(thePaddleMode == 4) + theEvent.set(Event::PaddleThreeFire, event.value); + } + } + else if((event.type & ~JS_EVENT_INIT) == JS_EVENT_AXIS) + { + if(event.number == 0) + { + theEvent.set(Event::JoystickOneLeft, (event.value < -16384) ? + 1 : keyboardEvent.get(Event::JoystickOneLeft)); + theEvent.set(Event::JoystickOneRight, (event.value > 16384) ? + 1 : keyboardEvent.get(Event::JoystickOneRight)); + + // If we're using real paddles then set paddle events as well + if(thePaddleMode == 4) + { + uInt32 r = (uInt32)((1.0E6L * (event.value + 32767L)) / 65536); + theEvent.set(Event::PaddleTwoResistance, r); + } + } + else if(event.number == 1) + { + theEvent.set(Event::JoystickOneUp, (event.value < -16384) ? + 1 : keyboardEvent.get(Event::JoystickOneUp)); + theEvent.set(Event::JoystickOneDown, (event.value > 16384) ? + 1 : keyboardEvent.get(Event::JoystickOneDown)); + + // If we're using real paddles then set paddle events as well + if(thePaddleMode == 4) + { + uInt32 r = (uInt32)((1.0E6L * (event.value + 32767L)) / 65536); + theEvent.set(Event::PaddleThreeResistance, r); + } + } + } + } + } +#endif +} + +/** + Display a usage message and exit the program +*/ +void usage() +{ + static const char* message[] = { + "", + "Usage: xstella [option ...] file", + "", + "Valid options are:", + "", + " -display Connect to the designated X display", + " -fps Display the given number of frames per second", + " -owncmap Install a private colormap", +#ifdef LINUX_JOYSTICK + " -paddle <0|1|2|3|real> Indicates which paddle the mouse should emulate", + " or that real Atari 2600 paddles are being used", +#else + " -paddle <0|1|2|3> Indicates which paddle the mouse should emulate", +#endif + "", + 0 + }; + + for(uInt32 i = 0; message[i] != 0; ++i) + { + cerr << message[i] << endl; + } + exit(1); +} + +/** + Setup the properties set by loading builtin defaults and then a + set of user specific ones from the file $HOME/.stella.pro + + @param set The properties set to setup +*/ +void setupProperties(PropertiesSet& set) +{ + // Try to load the file $HOME/.stella.pro file + string filename = getenv("HOME"); + filename += "/.stella.pro"; + + // See if we can open the file $HOME/.stella.pro + ifstream stream(filename.c_str()); + if(stream) + { + // File was opened so load properties from it + set.load(stream, &Console::defaultProperties()); + } + else + { + // Couldn't open the file so use the builtin properties file + strstream builtin; + for(const char** p = defaultPropertiesFile(); *p != 0; ++p) + { + builtin << *p << endl; + } + + set.load(builtin, &Console::defaultProperties()); + } +} + +/** + Should be called to parse the command line arguments + + @param argc The count of command line arguments + @param argv The command line arguments +*/ +void handleCommandLineArguments(int argc, char* argv[]) +{ + // Make sure we have the correct number of command line arguments + if((argc < 2) || (argc > 9)) + { + usage(); + } + + for(Int32 i = 1; i < (argc - 1); ++i) + { + // See which command line switch they're using + if(string(argv[i]) == "-fps") + { + // They're setting the desired frame rate + Int32 rate = atoi(argv[++i]); + if((rate < 1) || (rate > 300)) + { + rate = 60; + } + + theDesiredFrameRate = rate; + } + else if(string(argv[i]) == "-paddle") + { + // They're trying to set the paddle emulation mode + if(string(argv[i + 1]) == "real") + { + thePaddleMode = 4; + } + else + { + thePaddleMode = atoi(argv[i + 1]); + if((thePaddleMode < 0) || (thePaddleMode > 3)) + { + usage(); + } + } + ++i; + } + else if(string(argv[i]) == "-owncmap") + { + theUsePrivateColormapFlag = true; + } + else if(string(argv[i]) == "-display") + { + theDisplayName = argv[++i]; + } + else + { + usage(); + } + } +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +int main(int argc, char* argv[]) +{ + // Handle the command line arguments + handleCommandLineArguments(argc, argv); + + // Get a pointer to the file which contains the cartridge ROM + const char* file = argv[argc - 1]; + + // Open the cartridge image and read it in + ifstream in(file); + if(!in) + { + cerr << "ERROR: Couldn't open " << file << "..." << endl; + exit(1); + } + + uInt8* image = new uInt8[512 * 1024]; + in.read(image, 512 * 1024); + uInt32 size = in.gcount(); + in.close(); + + // Create a properties set for us to use and set it up + PropertiesSet propertiesSet("Cartridge.Name"); + setupProperties(propertiesSet); + + // Create a sound object for use with the console + SoundUnix sound; + + // Get just the filename of the file containing the ROM image + const char* filename = (!strrchr(file, '/')) ? file : strrchr(file, '/') + 1; + + // Create the 2600 game console + theConsole = new Console(image, size, filename, + theEvent, propertiesSet, sound); + + // Free the image since we don't need it any longer + delete[] image; + +#ifdef LINUX_JOYSTICK + // Open the joystick devices + theLeftJoystickFd = open("/dev/js0", O_RDONLY | O_NONBLOCK); + theRightJoystickFd = open("/dev/js1", O_RDONLY | O_NONBLOCK); +#endif + + // Setup X windows + setupX11(); + + // Get the starting time in case we need to print statistics + timeval startingTime; + gettimeofday(&startingTime, 0); + + uInt32 numberOfFrames = 0; + for( ; ; ++numberOfFrames) + { + // Exit if the user wants to quit + if(theQuitIndicator) + { + break; + } + + // Remember the current time before we start drawing the frame + timeval before; + gettimeofday(&before, 0); + + // Draw the frame and handle events + theConsole->mediaSource().update(); + updateDisplay(theConsole->mediaSource()); + handleEvents(); + + // Now, waste time if we need to so that we are at the desired frame rate + timeval after; + for(;;) + { + gettimeofday(&after, 0); + + uInt32 delta = (uInt32)((after.tv_sec - before.tv_sec) * 1000000 + + (after.tv_usec - before.tv_usec)); + + if(delta > (1000000 / theDesiredFrameRate)) + { + break; + } + } + } + + timeval endingTime; + gettimeofday(&endingTime, 0); + double executionTime = (endingTime.tv_sec - startingTime.tv_sec) + + ((endingTime.tv_usec - startingTime.tv_usec) / 1000000.0); + double framesPerSecond = numberOfFrames / executionTime; + + cout << endl; + cout << numberOfFrames << " total frames drawn\n"; + cout << framesPerSecond << " frames/second\n"; + cout << theConsole->mediaSource().scanlines() << " scanlines in last frame\n"; + cout << endl; + + delete theConsole; + + // If we're using a private colormap then let's free it to be safe + if(theUsePrivateColormapFlag) + { + XFreeColormap(theDisplay, thePrivateColormap); + } +} +