From a92a33b4da2d1d1018507011ea2fa0d2b090e9b9 Mon Sep 17 00:00:00 2001 From: Rob Bairos Date: Mon, 5 Apr 2021 08:42:39 -0400 Subject: [PATCH] MovieCart Streaming image and audio content created by presenting dynamic 1K of data on the bus, with On Screen Display. Volume, Brightness and Shuttle controlled by joystick and console switches. Original implementation: github.com/lodefmode/moviecart --- src/emucore/Bankswitch.cxx | 3 + src/emucore/Bankswitch.hxx | 4 +- src/emucore/CartCreator.cxx | 5 + src/emucore/CartDetector.cxx | 11 + src/emucore/CartDetector.hxx | 5 + src/emucore/CartMVC.cxx | 84 ++++ src/emucore/CartMVC.hxx | 181 +++++++ src/emucore/MovieCart/KernelData.hxx | 421 +++++++++++++++++ src/emucore/MovieCart/MovieCart.cxx | 626 +++++++++++++++++++++++++ src/emucore/MovieCart/MovieCart.hxx | 147 ++++++ src/emucore/MovieCart/MovieInputs.hxx | 91 ++++ src/emucore/MovieCart/StreamReader.cxx | 96 ++++ src/emucore/MovieCart/StreamReader.hxx | 135 ++++++ src/windows/Stella.vcxproj | 8 + src/windows/Stella.vcxproj.filters | 24 + 15 files changed, 1839 insertions(+), 2 deletions(-) create mode 100755 src/emucore/CartMVC.cxx create mode 100755 src/emucore/CartMVC.hxx create mode 100755 src/emucore/MovieCart/KernelData.hxx create mode 100755 src/emucore/MovieCart/MovieCart.cxx create mode 100755 src/emucore/MovieCart/MovieCart.hxx create mode 100755 src/emucore/MovieCart/MovieInputs.hxx create mode 100755 src/emucore/MovieCart/StreamReader.cxx create mode 100755 src/emucore/MovieCart/StreamReader.hxx diff --git a/src/emucore/Bankswitch.cxx b/src/emucore/Bankswitch.cxx index ba1f855a7..ce3fa69d9 100644 --- a/src/emucore/Bankswitch.cxx +++ b/src/emucore/Bankswitch.cxx @@ -140,6 +140,7 @@ Bankswitch::BSList = {{ { "FC" , "FC (32K Amiga)" }, { "FE" , "FE (8K Decathlon)" }, { "MDM" , "MDM (Menu Driven Megacart)" }, + { "MVC" , "MVC (Movie Cart)" }, { "SB" , "SB (128-256K SUPERbank)" }, { "TVBOY" , "TV Boy (512K)" }, { "UA" , "UA (8K UA Ltd.)" }, @@ -226,6 +227,7 @@ Bankswitch::ExtensionMap Bankswitch::ourExtensions = { { "FC" , Bankswitch::Type::_FC }, { "FE" , Bankswitch::Type::_FE }, { "MDM" , Bankswitch::Type::_MDM }, + { "MVC" , Bankswitch::Type::_MVC }, { "SB" , Bankswitch::Type::_SB }, { "TVB" , Bankswitch::Type::_TVBOY }, { "TVBOY" , Bankswitch::Type::_TVBOY }, @@ -284,6 +286,7 @@ Bankswitch::NameToTypeMap Bankswitch::ourNameToTypes = { { "FC" , Bankswitch::Type::_FC }, { "FE" , Bankswitch::Type::_FE }, { "MDM" , Bankswitch::Type::_MDM }, + { "MVC" , Bankswitch::Type::_MVC }, { "SB" , Bankswitch::Type::_SB }, { "TVBOY" , Bankswitch::Type::_TVBOY }, { "UA" , Bankswitch::Type::_UA }, diff --git a/src/emucore/Bankswitch.hxx b/src/emucore/Bankswitch.hxx index 3c1b2454a..2070ace36 100644 --- a/src/emucore/Bankswitch.hxx +++ b/src/emucore/Bankswitch.hxx @@ -44,8 +44,8 @@ class Bankswitch _CDF, _CM, _CTY, _CV, _DF, _DFSC, _DPC, _DPCP, _E0, _E7, _E78K, _EF, _EFSC, _F0, _F4, _F4SC, _F6, _F6SC, _F8, _F8SC, _FA, - _FA2, _FC, _FE, _MDM, _SB, _TVBOY, _UA, - _UASW, _WD, _WDSW, _X07, + _FA2, _FC, _FE, _MDM, _MVC, _SB, _TVBOY, + _UA, _UASW, _WD, _WDSW, _X07, #ifdef CUSTOM_ARM _CUSTOM, #endif diff --git a/src/emucore/CartCreator.cxx b/src/emucore/CartCreator.cxx index f792b449c..71cc118b8 100644 --- a/src/emucore/CartCreator.cxx +++ b/src/emucore/CartCreator.cxx @@ -55,6 +55,7 @@ #include "CartFC.hxx" #include "CartFE.hxx" #include "CartMDM.hxx" +#include "CartMVC.hxx" #include "CartSB.hxx" #include "CartTVBoy.hxx" #include "CartUA.hxx" @@ -197,6 +198,10 @@ unique_ptr CartCreator::create(const FilesystemNode& file, Bankswitch::typeToName(type) + "'"); break; + case Bankswitch::Type::_MVC: + cartridge = make_unique(file.getPath(), size, md5, settings); + break; + default: cartridge = createFromImage(image, size, detectedType, md5, settings); break; diff --git a/src/emucore/CartDetector.cxx b/src/emucore/CartDetector.cxx index 8d4f5f51b..6d5713f2e 100644 --- a/src/emucore/CartDetector.cxx +++ b/src/emucore/CartDetector.cxx @@ -240,6 +240,8 @@ Bankswitch::Type CartDetector::autodetectType(const ByteBuffer& image, size_t si type = Bankswitch::Type::_3EP; else if(isProbablyMDM(image, size)) type = Bankswitch::Type::_MDM; + else if(isProbablyMVC(image, size)) + type = Bankswitch::Type::_MVC; // If we get here and autodetection failed, then we force '4K' if(type == Bankswitch::Type::_AUTO) @@ -689,6 +691,15 @@ bool CartDetector::isProbablyMDM(const ByteBuffer& image, size_t size) return searchForBytes(image, std::min(size, 8_KB), mdmc, 4); } +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartDetector::isProbablyMVC(const ByteBuffer& image, size_t size) +{ + // MVC version 0, frame 0 + uInt8 sig[] = { 'M', 'V', 'C', 0 }; + return searchForBytes(image, std::min(size, 5), sig, 4); +} + + // - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - bool CartDetector::isProbablySB(const ByteBuffer& image, size_t size) { diff --git a/src/emucore/CartDetector.hxx b/src/emucore/CartDetector.hxx index 1ff16fc30..0061b1054 100644 --- a/src/emucore/CartDetector.hxx +++ b/src/emucore/CartDetector.hxx @@ -189,6 +189,11 @@ class CartDetector */ static bool isProbablyMDM(const ByteBuffer& image, size_t size); + /** + Returns true if the image is probably an MVC movie cartridge + */ + static bool isProbablyMVC(const ByteBuffer& image, size_t size); + /** Returns true if the image is probably a SB bankswitching cartridge */ diff --git a/src/emucore/CartMVC.cxx b/src/emucore/CartMVC.cxx new file mode 100755 index 000000000..ab05cd0b1 --- /dev/null +++ b/src/emucore/CartMVC.cxx @@ -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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#include "System.hxx" +#include "CartMVC.hxx" + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +CartridgeMVC::CartridgeMVC(const string& path, size_t size, + const string& md5, const Settings& settings, + size_t bsSize) + : Cartridge(settings, md5) +{ + myPath = path; + + // not used + mySize = 1024; + myImage = make_unique(mySize); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMVC::install(System& system) +{ + mySystem = &system; + + // Map all of the accesses to call peek and poke + System::PageAccess access(this, System::PageAccessType::READWRITE); + + access.directPeekBase = nullptr; + access.directPokeBase = nullptr; + + for(uInt16 addr = 0x1000; addr < 0x2000; addr += System::PAGE_SIZE) + mySystem->setPageAccess(addr, access); +} + + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +void CartridgeMVC::reset() +{ + myMovie.init(myPath); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +const ByteBuffer& CartridgeMVC::getImage(size_t& size) const +{ + // not used + size = mySize; + return myImage; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMVC::patch(uInt16 address, uInt8 value) +{ + myMovie.writeROM(address, value); + return true; +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +uInt8 CartridgeMVC::peek(uInt16 address) +{ + myMovie.process(address); + return myMovie.readROM(address); +} + +// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +bool CartridgeMVC::poke(uInt16 address, uInt8 value) +{ + return myMovie.process(address); +} + diff --git a/src/emucore/CartMVC.hxx b/src/emucore/CartMVC.hxx new file mode 100755 index 000000000..025940f44 --- /dev/null +++ b/src/emucore/CartMVC.hxx @@ -0,0 +1,181 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ + +#ifndef CARTRIDGEMVC_HXX +#define CARTRIDGEMVC_HXX + +class System; + +#include "bspf.hxx" +#include "Cart.hxx" +#include "MovieCart/MovieCart.hxx" + +/** + Implementation of MovieCart. + 1K of memory is presented on the bus, but is repeated to fill the 4K image space. + Contents are dynamically altered with streaming image and audio content as specific + 128-byte regions are entered. + Original implementation: github.com/lodefmode/moviecart + + @author Rob Bairos +*/ +class CartridgeMVC : public Cartridge +{ + + public: + /** + Create a new cartridge using the specified image + + @param image Pointer to the ROM image + @param size The size of the ROM image (<= 2048 bytes) + @param md5 The md5sum of the ROM image + @param settings A reference to the various settings (read-only) + @param bsSize The size specified by the bankswitching scheme + */ + CartridgeMVC(const string& path, size_t size, const string& md5, + const Settings& settings, size_t bsSize = 2_KB); + ~CartridgeMVC() override = default; + + /** + Reset device to its power-on state + */ + void reset() override; + + /** + 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 + */ + void install(System& system) override; + + /** + Access the internal ROM image for this cartridge. + + @param size Set to the size of the internal ROM image data + @return A reference to the internal ROM image data + */ + virtual const ByteBuffer& getImage(size_t& size) const; + + /** + Patch the cartridge ROM. + + @param address The ROM address to patch + @param value The value to place into the address + @return Success or failure of the patch operation + */ + virtual bool patch(uInt16 address, uInt8 value); + + /** + Get the byte at the specified address. + + @return The byte at the specified address + */ + uInt8 peek(uInt16 address) override; + + /** + 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 + @return True if the poke changed the device address space, else false + */ + bool poke(uInt16 address, uInt8 value) override; + + /** + Get a descriptor for the device name (used in error checking). + + @return The name of the object + */ + string name() const override { return "CartridgeMVC"; } + + /** + Save the current state of this cart to the given Serializer. + + @param out The Serializer object to use + @return False on any errors, else true + */ + bool save(Serializer& out) const override + { + return false; + } + + /** + Load the current state of this cart from the given Serializer. + + @param in The Serializer object to use + @return False on any errors, else true + */ + bool load(Serializer& in) override + { + return false; + } + + /** + Install pages for the specified bank in the system. + + @param bank The bank that should be installed in the system + @param segment The segment the bank should be using + + @return true, if bank has changed + */ + bool bank(uInt16 bank, uInt16 segment = 0) override + { + return false; + } + + /** + Get the current bank. + + @param address The address to use when querying the bank + */ + uInt16 getBank(uInt16 address = 0) const override + { + return 0; + } + + /** + Query the number of banks supported by the cartridge. + */ + uInt16 romBankCount() const override + { + return 1; + } + + private: + + // Currently not used: + // Pointer to a dynamically allocated ROM image of the cartridge + ByteBuffer myImage{nullptr}; + size_t mySize{0}; + + private: + // Following constructors and assignment operators not supported + CartridgeMVC() = delete; + CartridgeMVC(const CartridgeMVC&) = delete; + CartridgeMVC(CartridgeMVC&&) = delete; + CartridgeMVC& operator=(const CartridgeMVC&) = delete; + CartridgeMVC& operator=(CartridgeMVC&&) = delete; + + private: + + MovieCart myMovie; + string myPath; + +}; + +#endif diff --git a/src/emucore/MovieCart/KernelData.hxx b/src/emucore/MovieCart/KernelData.hxx new file mode 100755 index 000000000..6eae1f952 --- /dev/null +++ b/src/emucore/MovieCart/KernelData.hxx @@ -0,0 +1,421 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ +// Key kernel positions, automatically generated + +/** + Various kernel, OSD and scale definitions + @author Rob Bairos +*/ + +#pragma once + +#include + +#define TIMECODE_HEIGHT 12 +#define MAX_LEVEL 11 +#define DEFAULT_LEVEL 6 +#define BLANK_LINE_SIZE (28+3+37) // 68 + +#define addr_kernel_48 0x800 +#define addr_transport_buttons 0x880 +#define addr_transport_direction 0x897 +#define addr_right_line 0x94c +#define addr_left_line 0x980 +#define addr_pick_continue 0x9c2 +#define addr_main_start 0xa00 +#define addr_aud_bank_setup 0xa0c +#define addr_tg0 0xa24 +#define addr_title_again 0xa3b +#define addr_end_lines 0xa80 +#define addr_end_lines_audio 0xaa1 +#define addr_set_overscan_size 0xaad +#define addr_set_vblank_size 0xac3 +#define addr_pick_transport 0xacc +#define addr_last_audio 0xacf +#define addr_wait_lines 0xad4 +#define addr_transport_done1 0xae7 +#define addr_draw_title 0xb00 +#define addr_title_loop 0xb50 +#define addr_black_bar 0xb52 +#define addr_animate_bar1 0xb58 +#define addr_animate_bar_again1 0xb5a +#define addr_animate_dex1 0xb65 +#define addr_audio_bank 0xb80 +#define addr_reset_loop 0xbfa + +// scale adjustments, automatically generated +const uint8_t scale0[16] = { 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8, 8}; /* 0.0000 */ +const uint8_t scale1[16] = { 6, 6, 7, 7, 7, 7, 7, 7, 8, 8, 8, 8, 8, 8, 9, 9}; /* 0.1667 */ +const uint8_t scale2[16] = { 5, 5, 6, 6, 6, 7, 7, 7, 8, 8, 8, 9, 9, 9, 10, 10}; /* 0.3333 */ +const uint8_t scale3[16] = { 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11}; /* 0.5000 */ +const uint8_t scale4[16] = { 3, 3, 4, 5, 5, 6, 7, 7, 8, 9, 9, 10, 11, 11, 12, 13}; /* 0.6667 */ +const uint8_t scale5[16] = { 1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10, 10, 11, 12, 13, 14}; /* 0.8333 */ +const uint8_t scale6[16] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; /* 1.0000 */ +const uint8_t scale7[16] = { 0, 0, 0, 1, 3, 4, 5, 7, 8, 10, 11, 12, 14, 15, 15, 15}; /* 1.3611 */ +const uint8_t scale8[16] = { 0, 0, 0, 0, 1, 3, 5, 7, 8, 10, 12, 14, 15, 15, 15, 15}; /* 1.7778 */ +const uint8_t scale9[16] = { 0, 0, 0, 0, 0, 2, 4, 6, 9, 11, 13, 15, 15, 15, 15, 15}; /* 2.2500 */ +const uint8_t scale10[16] = { 0, 0, 0, 0, 0, 1, 3, 6, 9, 12, 14, 15, 15, 15, 15, 15}; /* 2.7778 */ +const uint8_t *scales[11] = {scale0, scale1, scale2, scale3, scale4, scale5, scale6, scale7, scale8, scale9, scale10}; + +// lower bit is ignored anyways +const uint8_t shiftBright[16 + MAX_LEVEL - 1] = { 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 15, 15, 15, 15}; + +// Compiled kernel +const unsigned char kernelROM[] = +{ + 133, 2, 185, 50, 248, 133, 27, 185, 62, 248, 133, 28, 185, 74, 248, 133, + 27, 185, 86, 248, 133, 135, 185, 98, 248, 190, 110, 248, 132, 136, 164, 135, + 132, 28, 133, 27, 134, 28, 134, 27, 164, 136, 102, 137, 176, 210, 136, 16, + 207, 96, 0, 1, 1, 1, 0, 0, 48, 48, 50, 53, 56, 48, 249, 129, + 129, 128, 248, 0, 99, 102, 102, 102, 230, 99, 140, 252, 140, 136, 112, 0, + 192, 97, 99, 102, 102, 198, 198, 198, 248, 198, 248, 0, 193, 32, 48, 24, + 24, 25, 24, 24, 24, 24, 126, 0, 249, 97, 97, 97, 97, 249, 0, 0, + 0, 0, 0, 0, 248, 128, 128, 224, 128, 248, 255, 255, 255, 255, 255, 255, + 165, 12, 10, 173, 130, 2, 42, 41, 23, 133, 129, 133, 128, 133, 128, 133, + 128, 133, 128, 133, 128, 234, 96, 173, 128, 2, 74, 74, 74, 133, 129, 133, + 128, 133, 128, 133, 128, 133, 128, 133, 128, 133, 128, 234, 96, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 133, 25, 133, 128, + 134, 32, 134, 33, 169, 243, 133, 27, 169, 207, 133, 28, 169, 251, 133, 27, + 169, 239, 162, 191, 160, 66, 133, 28, 169, 54, 133, 6, 134, 27, 169, 238, + 133, 7, 132, 6, 132, 28, 169, 182, 133, 6, 169, 114, 133, 7, 133, 42, + 162, 0, 162, 0, 169, 0, 162, 128, 133, 25, 134, 32, 134, 33, 169, 231, + 133, 27, 169, 159, 133, 28, 169, 253, 133, 27, 169, 247, 162, 223, 160, 242, + 133, 28, 169, 248, 133, 6, 134, 27, 169, 172, 133, 7, 132, 6, 132, 28, + 169, 216, 133, 6, 169, 98, 133, 7, 169, 0, 162, 0, 162, 0, 162, 0, + 133, 42, 76, 76, 249, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 120, 216, 162, 255, 154, 169, 0, 149, 0, 202, 208, 251, 169, 128, 133, 130, + 169, 251, 133, 131, 169, 1, 133, 37, 133, 38, 169, 3, 133, 4, 133, 5, + 133, 2, 162, 4, 133, 128, 202, 208, 251, 133, 128, 133, 16, 133, 17, 169, + 208, 133, 32, 169, 224, 133, 33, 133, 2, 133, 42, 165, 132, 106, 106, 133, + 6, 133, 7, 169, 85, 133, 137, 32, 0, 251, 176, 239, 169, 0, 133, 9, + 141, 129, 2, 141, 131, 2, 169, 6, 133, 4, 169, 2, 133, 5, 169, 0, + 133, 2, 133, 128, 76, 128, 250, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 133, 25, 169, 0, 133, 27, 133, 28, 133, 27, 133, 28, 133, 128, 133, 128, + 133, 128, 169, 0, 133, 16, 234, 133, 17, 169, 208, 133, 32, 169, 192, 133, + 33, 169, 0, 133, 2, 133, 42, 133, 128, 133, 25, 160, 0, 162, 28, 32, + 212, 250, 169, 2, 133, 0, 162, 3, 32, 212, 250, 169, 0, 133, 0, 169, + 2, 133, 1, 162, 37, 32, 212, 250, 162, 0, 134, 1, 32, 128, 248, 169, + 0, 76, 76, 249, 177, 130, 133, 2, 133, 128, 133, 128, 133, 25, 165, 129, + 240, 5, 198, 129, 173, 128, 20, 200, 202, 208, 233, 96, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 162, 30, 32, 82, 251, 169, 2, 133, 0, 162, 3, 32, 82, 251, 169, 0, + 133, 0, 169, 2, 133, 1, 162, 37, 32, 82, 251, 169, 0, 133, 1, 198, + 132, 165, 132, 133, 133, 160, 255, 162, 30, 32, 88, 251, 162, 54, 32, 82, + 251, 160, 11, 32, 0, 248, 169, 0, 133, 27, 133, 28, 133, 27, 133, 28, + 162, 54, 32, 82, 251, 165, 132, 133, 133, 160, 1, 162, 30, 32, 88, 251, + 56, 96, 169, 0, 133, 133, 160, 0, 132, 134, 24, 165, 133, 101, 134, 133, + 133, 133, 2, 133, 9, 202, 208, 242, 96, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, + 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 0, 250, 0, 250, 0, 250, +}; + +// OSD labels + +const uint8_t brightLabelEven[] = +{ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 225, 48, 12, 252, + 6, 140, 231, 96, 0, + 0, 113, 48, 12, 96, + 6, 140, 192, 96, 0, + 0, 225, 49, 15, 96, + 6, 152, 195, 96, 0, + 0, 49, 48, 12, 96, + 6, 140, 231, 96, 0, + 0, 225, 48, 12, 96, +}; + +const uint8_t brightLabelOdd[] = +{ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 7, 252, 126, 99, 0, + 0, 113, 48, 12, 96, + 6, 140, 192, 96, 0, + 0, 97, 49, 12, 96, + 7, 248, 223, 224, 0, + 0, 113, 49, 12, 96, + 6, 156, 195, 96, 0, + 0, 113, 48, 12, 96, + 7, 142, 127, 96, 0, + 0, 0, 0, 0, 0 +}; + +const uint8_t volumeLabelEven[] = +{ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 0, 199, 192, 14, 254, + 113, 112, 99, 112, 0, + 0, 140, 192, 14, 192, + 51, 48, 99, 240, 0, + 0, 28, 192, 15, 254, + 31, 48, 99, 240, 0, + 0, 12, 192, 15, 192, + 30, 112, 119, 176, 0, + 0, 7, 252, 12, 254, +}; + +const uint8_t volumeLabelOdd[] = +{ + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + 97, 224, 99, 112, 0, + 0, 142, 192, 14, 192, + 51, 112, 99, 112, 0, + 0, 28, 192, 15, 192, + 59, 48, 99, 240, 0, + 0, 28, 192, 15, 192, + 30, 112, 99, 176, 0, + 0, 14, 192, 13, 192, + 14, 224, 62, 48, 0, + 0, 0, 0, 0, 0 +}; + +// Level bars +// 8 rows * 5 columns = 40 + +const uint8_t levelBarsEvenData[] = +{ + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 2, 40, 161, 133, 20, + 20, 80, 66, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 40, 161, 133, 20, + 244, 80, 66, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 232, 161, 133, 20, + 247, 80, 66, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 161, 133, 20, + 247, 208, 66, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 161, 133, 20, + 247, 223, 66, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 133, 20, + 247, 223, 66, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 133, 20, + 247, 223, 126, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 20, + 247, 223, 126, 10, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 20, + 247, 223, 126, 250, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 244, + 247, 223, 126, 251, 32, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + /**/ + 0, 0, 0, 0, 0, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 0, 0, 0, 0, 0, + +}; + +const uint8_t levelBarsOddData[] = +{ + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 20, 80, 66, 10, 32, + 2, 40, 161, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 244, 80, 66, 10, 32, + 3, 40, 161, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 80, 66, 10, 32, + 3, 232, 161, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 208, 66, 10, 32, + 3, 239, 161, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 66, 10, 32, + 3, 239, 161, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 66, 10, 32, + 3, 239, 191, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 10, 32, + 3, 239, 191, 133, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 10, 32, + 3, 239, 191, 253, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 250, 32, + 3, 239, 191, 253, 20, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 32, + 3, 239, 191, 253, 244, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, + /**/ + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 247, 223, 126, 251, 224, + 3, 239, 191, 253, 247, + 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, +}; + diff --git a/src/emucore/MovieCart/MovieCart.cxx b/src/emucore/MovieCart/MovieCart.cxx new file mode 100755 index 000000000..c30b65482 --- /dev/null +++ b/src/emucore/MovieCart/MovieCart.cxx @@ -0,0 +1,626 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ +// Key kernel positions, automatically generated + +/** + Implementation of MovieCart. + 1K of memory is presented on the bus, but is repeated to fill the 4K image space. + Contents are dynamically altered with streaming image and audio content as specific + 128-byte regions are entered. + Original implementation: github.com/lodefmode/moviecart + + @author Rob Bairos +*/ + +#include +#include "KernelData.hxx" +#include "MovieCart.hxx" + +#define LO_JUMP_BYTE(X) ((X) & 0xff) +#define HI_JUMP_BYTE(X) ((((X) & 0xff00) >> 8) | 0x10) + +#define COLOR_BLUE 0x9A +#define COLOR_WHITE 0x0E + +#define OSD_FRAMES 180 +#define BACK_SECONDS 10 + +#define TITLE_CYCLES 1000000 + +bool +MovieCart::init(const std::string& path) +{ + memcpy(myROM, kernelROM, 1024); + + myTitleCycles = 0; + myTitleState = TitleState::Display; + + myA7 = false; + myA10 = false; + myA10_Count = 0; + + myState = 3; + myPlaying = true; + myOdd = true; + myBufferIndex = false; + myFrameNumber = 1; + + myInputs.init(); + myLastInputs.init(); + mySpeed = 1; + myJoyRepeat = 0; + myDirectionValue = 0; + myButtonsValue = 0; + + myLines = 0; + myForceColor = 0; + myDrawLevelBars = 0; + myDrawTimeCode = 0; + myFirstAudioVal = 0; + + myMode = Mode::Volume; + myVolume = DEFAULT_LEVEL; + myVolumeScale = scales[DEFAULT_LEVEL]; + myBright = DEFAULT_LEVEL; + + if (!myStream.open(path)) + return false; + + myStream.swapField(true); + + return true; +} + +void +MovieCart::stopTitleScreen() +{ + writeROM(addr_title_loop + 0, 0x18); // clear carry, one bit difference from 0x38 sec +} + + +void +MovieCart::writeColor(uint16_t address) +{ + uint8_t v = myStream.readColor(); + v = (v & 0xf0) | shiftBright[(v & 0x0f) + myBright]; + + if (myForceColor) + v = myForceColor; + if (myInputs.bw) + v &= 0x0f; + + writeROM(address, v); +} + +void +MovieCart::writeAudioData(uint16_t address, uint8_t val) +{ + uint8_t v; + v = myVolumeScale[val]; + writeROM(address, v); +} + +void +MovieCart::writeAudio(uint16_t address) +{ + uint8_t v = myStream.readAudio(); + writeAudioData(address, v); +} + +void +MovieCart::writeGraph(uint16_t address) +{ + uint8_t v = myStream.readGraph(); + writeROM(address, v); +} + +void +MovieCart::updateTransport() +{ + myStream.overrideGraph(nullptr); + + + // have to cut rate in half, to remove glitches...todo.. + { + if (myBufferIndex == true) + { + uint8_t temp = ~(myA10_Count & 0x1e) & 0x1e; + + if (temp == myDirectionValue) + myInputs.updateDirection(temp); + + myDirectionValue = temp; + } + else + { + uint8_t temp = ~(myA10_Count & 0x17) & 0x17; + + if (temp == myButtonsValue) + myInputs.updateTransport(temp); + + myButtonsValue = temp; + } + + myA10_Count = 0; + } + + if (myInputs.reset) + { + myFrameNumber = 1; + myPlaying = true; + myDrawTimeCode = OSD_FRAMES; + + goto update_stream; + } + + uint8_t lastMainMode = myMode; + + if (myInputs.up && !myLastInputs.up) + { + if (myMode == 0) + myMode = Mode::Last; + else + myMode--; + } + else if (myInputs.down && !myLastInputs.down) + { + if (myMode == Mode::Last) + myMode = 0; + else + myMode++; + } + + if (myInputs.left || myInputs.right) + { + myJoyRepeat++; + } + else + { + myJoyRepeat = 0; + mySpeed = 1; + } + + + if (myJoyRepeat & 16) + { + myJoyRepeat = 0; + + if (myInputs.left || myInputs.right) + { + if (myMode == Mode::Time) + { + myDrawTimeCode = OSD_FRAMES; + mySpeed += 4; + if (mySpeed < 0) + mySpeed -= 4; + } + else if (myMode == Mode::Volume) + { + myDrawLevelBars = OSD_FRAMES; + if (myInputs.left) + { + if (myVolume) + myVolume--; + } + else + { + myVolume++; + if (myVolume >= MAX_LEVEL) + myVolume--; + } + } + else if (myMode == Mode::Bright) + { + myDrawLevelBars = OSD_FRAMES; + if (myInputs.left) + { + if (myBright) + myBright--; + } + else + { + myBright++; + if (myBright >= MAX_LEVEL) + myBright--; + } + } + } + } + + + if (myInputs.select && !myLastInputs.select) + { + myDrawTimeCode = OSD_FRAMES; + myFrameNumber -= 60 * BACK_SECONDS + 1; + goto update_stream; + } + + if (myInputs.fire && !myLastInputs.fire) + myPlaying = !myPlaying; + + switch (myMode) + { + case Mode::Time: + if (lastMainMode != myMode) + myDrawTimeCode = OSD_FRAMES; + break; + + case Mode::Bright: + case Mode::Volume: + default: + if (lastMainMode != myMode) + myDrawLevelBars = OSD_FRAMES; + break; + } + + // just draw one + if (myDrawLevelBars > myDrawTimeCode) + myDrawTimeCode = 0; + else + myDrawLevelBars = 0; + + if (myPlaying) + myVolumeScale = scales[myVolume]; + else + myVolumeScale = scales[0]; + + // update frame + + int8_t step = 1; + + if (!myPlaying) // step while paused + { + if (myMode == Mode::Time) + { + if (myInputs.right && !myLastInputs.right) + step = 3; + else if (myInputs.left && !myLastInputs.left) + step = -3; + else + step = (myFrameNumber & 1) ? -1 : 1; + } + else + { + step = (myFrameNumber & 1) ? -1 : 1; + } + } + else + { + if (myMode == Mode::Time) + { + if (myInputs.right) + step = mySpeed; + else if (myInputs.left) + step = -mySpeed; + } + else + { + step = 1; + } + } + + myFrameNumber += step; + if (myFrameNumber < 1) + { + myFrameNumber = 1; + mySpeed = 1; + } + +update_stream: + + myLastInputs = myInputs; + +} + +void +MovieCart::fill_addr_right_line() +{ + writeGraph(addr_right_line + 9); // #GDATA0 + writeGraph(addr_right_line + 13); // #GDATA1 + writeGraph(addr_right_line + 17); // #GDATA2 + writeGraph(addr_right_line + 21); // #GDATA3 + writeGraph(addr_right_line + 23); // #GDATA4 + + writeColor(addr_right_line + 25); // #GCOL0 + writeColor(addr_right_line + 29); // #GCOL1 + writeColor(addr_right_line + 35); // #GCOL2 + writeColor(addr_right_line + 43); // #GCOL3 + writeColor(addr_right_line + 47); // #GCOL4 +} + +void +MovieCart::fill_addr_left_line(bool again) +{ + writeAudio(addr_left_line + 5); // #AUD_DATA + + writeGraph(addr_left_line + 15); // #GDATA5 + writeGraph(addr_left_line + 19); // #GDATA6 + writeGraph(addr_left_line + 23); // #GDATA7 + writeGraph(addr_left_line + 27); // #GDATA8 + writeGraph(addr_left_line + 29); // #GDATA9 + + writeColor(addr_left_line + 31); // #GCOL5 + writeColor(addr_left_line + 35); // #GCOL6 + writeColor(addr_left_line + 41); // #GCOL7 + writeColor(addr_left_line + 49); // #GCOL8 + writeColor(addr_left_line + 53); // #GCOL9 + + writeAudio(addr_left_line + 57); // #AUD_DATA + + // addr_pick_line_end = 0x0ee; + // jmp right_line + // jmp end_lines + if (again) + { + writeROM(addr_pick_continue + 1, LO_JUMP_BYTE(addr_right_line)); + writeROM(addr_pick_continue + 2, HI_JUMP_BYTE(addr_right_line)); + } + else + { + writeROM(addr_pick_continue + 1, LO_JUMP_BYTE(addr_end_lines)); + writeROM(addr_pick_continue + 2, HI_JUMP_BYTE(addr_end_lines)); + } +} + + +void +MovieCart::fill_addr_end_lines() +{ + writeAudio(addr_end_lines_audio + 1); + myFirstAudioVal = myStream.peekAudio(); + + // normally overscan=28, vblank=37 + // todo: clicky noise.. + if (myOdd) + { + writeROM(addr_set_overscan_size + 1, 28); + writeROM(addr_set_vblank_size + 1, 36); + } + else + { + writeROM(addr_set_overscan_size + 1, 29); + writeROM(addr_set_vblank_size + 1, 37); + } + + if (myBufferIndex == false) + { + writeROM(addr_pick_transport + 1, LO_JUMP_BYTE(addr_transport_direction)); + writeROM(addr_pick_transport + 2, HI_JUMP_BYTE(addr_transport_direction)); + } + else + { + writeROM(addr_pick_transport + 1, LO_JUMP_BYTE(addr_transport_buttons)); + writeROM(addr_pick_transport + 2, HI_JUMP_BYTE(addr_transport_buttons)); + } + +} + +void +MovieCart::fill_addr_blank_lines() +{ + uint8_t i; + uint8_t v; + + // version number + myStream.readVersion(); + myStream.readVersion(); + myStream.readVersion(); + myStream.readVersion(); + + // frame number + myStream.readFrame(); + myStream.readFrame(); + v = myStream.readFrame(); + + // make sure we're in sync with frame data + myOdd = (v & 1); + + // 28 overscan + // 3 vsync + // 37 vblank + + if (myOdd) + { + writeAudioData(addr_audio_bank + 0, myFirstAudioVal); + for (i = 1; i < (BLANK_LINE_SIZE + 1); i++) + writeAudio(addr_audio_bank + i); + } + else + { + for (i = 0; i < (BLANK_LINE_SIZE -1); i++) + writeAudio(addr_audio_bank + i); + } + + writeAudio(addr_last_audio + 1); +} + +void +MovieCart::runStateMachine() +{ + switch(myState) + { + case 1: + if (myA7) + { + if (myLines == (TIMECODE_HEIGHT-1)) + { + if (myDrawTimeCode) + { + myDrawTimeCode--; + myForceColor = COLOR_BLUE; + myStream.startTimeCode(); + } + } + + // label = 12, bars = 7 + if (myLines == 21) + { + if (myDrawLevelBars) + { + myDrawLevelBars--; + myForceColor = COLOR_BLUE; + + switch (myMode) + { + case Mode::Time: + myStream.overrideGraph(nullptr); + break; + + case Mode::Bright: + if (myOdd) + myStream.overrideGraph(brightLabelOdd); + else + myStream.overrideGraph(brightLabelEven); + break; + + case Mode::Volume: + default: + if (myOdd) + myStream.overrideGraph(volumeLabelOdd); + else + myStream.overrideGraph(volumeLabelEven); + break; + } + } + } + + if (myLines == 7) + { + if (myDrawLevelBars) + { + uint8_t levelValue; + + switch (myMode) + { + case Mode::Time: + levelValue = 0; + break; + + case Mode::Bright: + levelValue = myBright; + break; + + case Mode::Volume: + default: + levelValue = myVolume; + break; + } + + if (myOdd) + myStream.overrideGraph(&levelBarsOddData[levelValue * 40]); + else + myStream.overrideGraph(&levelBarsEvenData[levelValue * 40]); + } + } + + fill_addr_right_line(); + + myLines -= 1; + myState = 2; + } + break; + + + case 2: + if (!myA7) + { + if (myLines >= 1) + { + fill_addr_left_line(1); + + myLines -= 1; + myState = 1; + } + else + { + fill_addr_left_line(0); + fill_addr_end_lines(); + + myStream.swapField(myBufferIndex); + myBufferIndex = !myBufferIndex; + updateTransport(); + + fill_addr_blank_lines(); + + myState = 3; + } + } + break; + + case 3: + if (myA7) + { + // hit end? rewind just before end + while (myFrameNumber >= 2 && !myStream.readField(myFrameNumber, myBufferIndex)) + { + myFrameNumber -= 2; + myJoyRepeat = 0; + } + + myForceColor = 0; + myLines = 191; + myState = 1; + } + break; + + default: + break; + } +} + +bool +MovieCart::process(uint16_t address) +{ + + bool a12 = (address & (1 << 12)) ? 1:0; + bool a11 = (address & (1 << 11)) ? 1:0; + + // count a10 pulses + bool a10i = (address & (1 << 10)); + if (a10i && !myA10) + myA10_Count++; + myA10 = a10i; + + // latch a7 state + if (a11) // a12 + myA7 = (address & (1 << 7)); // each 128 + + switch(myTitleState) + { + case TitleState::Display: + myTitleCycles++; + if (myTitleCycles == TITLE_CYCLES) + { + stopTitleScreen(); + myTitleState = TitleState::Exiting; + myTitleCycles = 0; + } + break; + + case TitleState::Exiting: + if (myA7) + myTitleState = TitleState::Stream; + break; + + case TitleState::Stream: + runStateMachine(); + break; + } + + return a12; +} + diff --git a/src/emucore/MovieCart/MovieCart.hxx b/src/emucore/MovieCart/MovieCart.hxx new file mode 100755 index 000000000..b58c1c0db --- /dev/null +++ b/src/emucore/MovieCart/MovieCart.hxx @@ -0,0 +1,147 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ +// Key kernel positions, automatically generated + +/** + Implementation of MovieCart. + 1K of memory is presented on the bus, but is repeated to fill the 4K image space. + Contents are dynamically altered with streaming image and audio content as specific + 128-byte regions are entered. + Original implementation: github.com/lodefmode/moviecart + + @author Rob Bairos +*/ + +#pragma once + +#include +#include "StreamReader.hxx" +#include "MovieInputs.hxx" + +class MovieCart +{ + +public: + + MovieCart() + { + } + + ~MovieCart() + { + } + + bool init(const std::string& path); + bool process(uint16_t address); + + uint8_t + readROM(uint16_t address) + { + return myROM[address & 1023]; + } + + void + writeROM(uint16_t address, uint8_t data) + { + myROM[address & 1023] = data; + } + +private: + + enum Mode + { + Volume, + Bright, + Time, + Last = Time + }; + + enum TitleState + { + Display, + Exiting, + Stream + }; + + + void stopTitleScreen(); + + void writeColor(uint16_t address); + void writeAudioData(uint16_t address, uint8_t val); + void writeAudio(uint16_t address); + void writeGraph(uint16_t address); + + void runStateMachine(); + + void fill_addr_right_line(); + void fill_addr_left_line(bool again); + void fill_addr_end_lines(); + void fill_addr_blank_lines(); + + void updateTransport(); + + + StreamReader myStream; + + // data + + uint8_t myROM[1024]; + + + // title screen state + int myTitleCycles; + uint8_t myTitleState; + + + // address info + bool myA7; + bool myA10; + uint8_t myA10_Count; + + // state machine info + + uint8_t myState; + bool myPlaying; + bool myOdd; + bool myBufferIndex; + + + uint8_t myLines; + int32_t myFrameNumber; // signed + + uint8_t myMode; + uint8_t myBright; + uint8_t myForceColor; + + // expressed in frames + uint8_t myDrawLevelBars; + uint8_t myDrawTimeCode; + + MovieInputs myInputs; + MovieInputs myLastInputs; + int8_t mySpeed; // signed + uint8_t myJoyRepeat; + uint8_t myDirectionValue; + uint8_t myButtonsValue; + + uint8_t myVolume; + const uint8_t* myVolumeScale; + uint8_t myFirstAudioVal; + +}; + + diff --git a/src/emucore/MovieCart/MovieInputs.hxx b/src/emucore/MovieCart/MovieInputs.hxx new file mode 100755 index 000000000..20c31f0e5 --- /dev/null +++ b/src/emucore/MovieCart/MovieInputs.hxx @@ -0,0 +1,91 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ +// Key kernel positions, automatically generated + +/** + State of current switches and joystick positions to control MovieCart + @author Rob Bairos +*/ + +#pragma once + +class MovieInputs +{ +public: + + MovieInputs() + { + init(); + } + + void + init() + { + bw = false; + fire = false; + select = false; + reset = false; + + right = false; + left = false; + up = false; + down = false; + } + + bool bw; + bool fire; + bool select; + bool reset; + + bool right; + bool left; + bool up; + bool down; + + void + updateDirection(uint8_t val) + { + right = val & TRANSPORT_RIGHT; + left = val & TRANSPORT_LEFT; + up = val & TRANSPORT_UP; + down = val & TRANSPORT_DOWN; + } + + void updateTransport(uint8_t val) + { + bw = val & TRANSPORT_BW; + fire = val & TRANSPORT_BUTTON; + select = val & TRANSPORT_SELECT; + reset = val & TRANSPORT_RESET; + } + +private: + + static int constexpr TRANSPORT_RIGHT = 0x10; + static int constexpr TRANSPORT_LEFT = 0x08; + static int constexpr TRANSPORT_DOWN = 0x04; + static int constexpr TRANSPORT_UP = 0x02; + static int constexpr TRANSPORT_UNUSED1 = 0x01; // Right-2 + + static int constexpr TRANSPORT_BW = 0x10; + static int constexpr TRANSPORT_UNUSED2 = 0x08; + static int constexpr TRANSPORT_SELECT = 0x04; + static int constexpr TRANSPORT_RESET = 0x02; + static int constexpr TRANSPORT_BUTTON = 0x01; + +}; + diff --git a/src/emucore/MovieCart/StreamReader.cxx b/src/emucore/MovieCart/StreamReader.cxx new file mode 100755 index 000000000..9029b5858 --- /dev/null +++ b/src/emucore/MovieCart/StreamReader.cxx @@ -0,0 +1,96 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ +// Key kernel positions, automatically generated + +/** + Simulate retrieval of 512 byte chunks from a serial source + @author Rob Bairos +*/ + +#include "StreamReader.hxx" + +bool +StreamReader::open(const std::string& path) +{ + close(); + + myFile = new std::ifstream(path, std::ios::binary); + + if (!myFile || !*myFile) + close(); + + myFile->seekg(0, std::ios::end); + myFileSize = static_cast(myFile->tellg()); + myFile->seekg(0, std::ios::beg); + + return myFile ? true:false; +} + +void +StreamReader::close() +{ + delete myFile; + myFile = nullptr; +} + +void +StreamReader::swapField(bool index) +{ + if (index == true) + { + myVersion = myBuffer1 + VERSION_DATA_OFFSET; + myFrame = myBuffer1 + FRAME_DATA_OFFSET; + myAudio = myBuffer1 + AUDIO_DATA_OFFSET; + myGraph = myBuffer1 + GRAPH_DATA_OFFSET; + myTimecode= myBuffer1 + TIMECODE_DATA_OFFSET; + myColor = myBuffer1 + COLOR_DATA_OFFSET; + } + else + { + myVersion = myBuffer2 + VERSION_DATA_OFFSET; + myFrame = myBuffer2 + FRAME_DATA_OFFSET; + myAudio = myBuffer2 + AUDIO_DATA_OFFSET; + myGraph = myBuffer2 + GRAPH_DATA_OFFSET; + myTimecode = myBuffer2 + TIMECODE_DATA_OFFSET; + myColor = myBuffer2 + COLOR_DATA_OFFSET; + } +} + +bool +StreamReader::readField(uint32_t fnum, bool index) +{ + bool read = false; + + if (myFile) + { + size_t offset = ((fnum + 0) * MVC_FIELD_PAD_SIZE); + + if (offset + MVC_FIELD_PAD_SIZE < myFileSize) + { + myFile->seekg(offset); + if (index == true) + myFile->read((char*)myBuffer1, MVC_FIELD_SIZE); + else + myFile->read((char*)myBuffer2, MVC_FIELD_SIZE); + + read = true; + } + } + + return read; +} + diff --git a/src/emucore/MovieCart/StreamReader.hxx b/src/emucore/MovieCart/StreamReader.hxx new file mode 100755 index 000000000..61a0b33e1 --- /dev/null +++ b/src/emucore/MovieCart/StreamReader.hxx @@ -0,0 +1,135 @@ +//============================================================================ +// +// 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-2021 by Bradford W. Mott, Stephen Anthony +// and the Stella Team +// +// See the file "License.txt" for information on usage and redistribution of +// this file, and for a DISCLAIMER OF ALL WARRANTIES. +//============================================================================ +// Key kernel positions, automatically generated + +/** + Simulate retrieval 512 byte chunks from a serial source + @author Rob Bairos +*/ + +#pragma once + +#include +#include +#include + +#define MVC_FIELD_SIZE 2560 // round field to nearest 512 byte boundary +#define MVC_FIELD_PAD_SIZE 4096 // round to nearest 4K + +class StreamReader +{ +public: + + StreamReader() + { + } + + ~StreamReader() + { + close(); + } + + bool open(const std::string& path); + void close(); + void swapField(bool index); + bool readField(uint32_t fnum, bool index); + + uint8_t + readVersion() + { + return *myVersion++; + } + + uint8_t + readFrame() + { + return *myFrame++; + } + + uint8_t + readColor() + { + return *myColor++; + } + + uint8_t + readGraph() + { + uint8_t v; + + if (myGraphOverride) + v = *myGraphOverride++; + else + v = *myGraph++; + + return v; + } + + void + overrideGraph(const uint8_t* p) + { + myGraphOverride = p; + } + + uint8_t + readAudio() + { + return *myAudio++; + } + + uint8_t + peekAudio() + { + return *myAudio; + } + + void + startTimeCode() + { + myGraph = myTimecode; + } + + +private: + + static int constexpr VERSION_DATA_OFFSET = 0; + static int constexpr FRAME_DATA_OFFSET = 4; + static int constexpr AUDIO_DATA_OFFSET = 7; + static int constexpr GRAPH_DATA_OFFSET = 269; + static int constexpr TIMECODE_DATA_OFFSET = 1229; + static int constexpr COLOR_DATA_OFFSET = 1289; + static int constexpr END_DATA_OFFSET = 2249; + + + const uint8_t* myAudio{nullptr}; + + const uint8_t* myGraph{nullptr}; + const uint8_t* myGraphOverride{nullptr}; + + const uint8_t* myTimecode{nullptr}; + const uint8_t* myColor{nullptr}; + const uint8_t* myVersion{nullptr}; + const uint8_t* myFrame{nullptr}; + + uint8_t myBuffer1[MVC_FIELD_SIZE]; + uint8_t myBuffer2[MVC_FIELD_SIZE]; + + std::ifstream* myFile{nullptr}; + size_t myFileSize{0}; + +}; + diff --git a/src/windows/Stella.vcxproj b/src/windows/Stella.vcxproj index edf8751ee..1d890a299 100644 --- a/src/windows/Stella.vcxproj +++ b/src/windows/Stella.vcxproj @@ -753,6 +753,7 @@ + @@ -763,6 +764,8 @@ + + @@ -1817,6 +1820,7 @@ + @@ -1833,6 +1837,10 @@ + + + + diff --git a/src/windows/Stella.vcxproj.filters b/src/windows/Stella.vcxproj.filters index f65dd22bb..67c3394df 100644 --- a/src/windows/Stella.vcxproj.filters +++ b/src/windows/Stella.vcxproj.filters @@ -1110,6 +1110,15 @@ Source Files\repository + + Source Files\emucore + + + Source Files\emucore + + + Source Files\emucore + @@ -2282,6 +2291,21 @@ Header Files\emucore + + Header Files\emucore + + + Header Files\emucore + + + Header Files\emucore + + + Header Files\emucore + + + Header Files\emucore +