1//============================================================================
2//
3// SSSS tt lll lll
4// SS SS tt ll ll
5// SS tttttt eeee ll ll aaaa
6// SSSS tt ee ee ll ll aa
7// SS tt eeeeee ll ll aaaaa -- "An Atari 2600 VCS Emulator"
8// SS SS tt ee ll ll aa aa
9// SSSS ttt eeeee llll llll aaaaa
10//
11// Copyright (c) 1995-2019 by Bradford W. Mott, Stephen Anthony
12// and the Stella Team
13//
14// See the file "License.txt" for information on usage and redistribution of
15// this file, and for a DISCLAIMER OF ALL WARRANTIES.
16//============================================================================
17
18#ifndef CARTRIDGECHETIRY_HXX
19#define CARTRIDGECHETIRY_HXX
20
21class System;
22
23#include "bspf.hxx"
24#include "Cart.hxx"
25#ifdef DEBUGGER_SUPPORT
26 #include "CartCTYWidget.hxx"
27#endif
28
29/**
30 The 'Chetiry' bankswitch scheme was developed by Chris D. Walton for a
31 Tetris clone game by the same name. It makes use of a Harmony cart,
32 whereby ARM code in bank 0 is executed to implement the bankswitch scheme.
33 The implementation here does not execute this ARM code, and instead
34 implements the bankswitching directly. Its functionality is similar to
35 several other schemes, as follows:
36
37 F4SC:
38 The scheme contains 8 4K banks, with the first bank being inaccessible
39 (due to containing ARM code). The remaining banks (1 - 7) are accessed
40 at hotspots $FF5 - $FFB, exactly the same as F4SC.
41
42 There is 64 bytes of RAM (vs. 128 bytes in F4SC) at $1000 - $107F
43 ($1000 - $103F is write port, $1040 - $107F is read port).
44
45 FA2:
46 The first four bytes of RAM are actually a kind of hotspot, with the
47 following functionality. Data is accessed from Harmony EEPROM in
48 the same fashion as the FA2 scheme.
49
50 Write Addresses:
51 $1000 = Operation Type (see discussion of hotspot $1FF4 below)
52 $1001 = Set Random Seed Value
53 $1002 = Reset Fetcher To Beginning Of Tune
54 $1003 = Advance Fetcher To Next Tune Position
55
56 Read Addresses:
57 $1040 = Error Code after operation
58 $1041 = Get Next Random Number (8-bit LFSR)
59 $1042 = Get Tune Position (Low Byte)
60 $1043 = Get Tune Position (High Byte)
61
62 RAM Load And Save Operations:
63
64 Address $1FF4 is used as a special hotspot to trigger loading and saving
65 of the RAM, similar to FA2 bankswitching. The operation to perform is
66 given using the first byte of the extra RAM. The format of this byte is
67 XXXXYYYY, where XXXX is an index and YYYY is the operation to perform.
68 There are 4 different operation types:
69
70 1 = Load Tune (index = tune)
71 2 = Load Score Table (index = table)
72 3 = Save Score Table (index = table)
73 4 = Wipe All Score Tables (set all 256 bytes of EEPROM to $00)
74
75 The score table functionality is based on 256 bytes from Harmony
76 EEPROM, of which there are 4 64-byte 'tables'. The 'index' for
77 operations 2 and 3 can therefore be in the range 0 - 3, indicating
78 which table to use. For this implementation, the 256 byte EEPROM
79 is serialized to a file.
80
81 The tune table functionality is also based on Harmony EEPROM, where
82 7 4K tunes are stored (28K total). The 'index' for operation 1 can
83 therefore be in the range 0 - 6, indicating which tune to load.
84
85 DPC+:
86 The music functionality is quite similar to the DPC+ scheme.
87
88 Fast Fetcher
89 The music frequency value is fetched using a fast fetcher operation.
90 This operation is aliased to the instruction "LDA #$F2". Whenever this
91 instruction is executed, the $F2 value is replaced with the frequency
92 value calculated from the tune data. The pointer to the tune data does
93 not advance until address $1003 is written. When a new tune is loaded,
94 the pointer is reset to the beginning of the tune. This also happens
95 when the end of the tune is reached or when address $1002 is written to.
96
97 The calculation of the frequency value is essentially the same as DPC.
98 There are 3 different channels that are combined together, and only a
99 square waveform is used. The data is formatted so that the three notes
100 for each position appear consecutively (note0, note1, note2). Moving
101 to the next tune position means incrementing by 3 bytes. The end of the
102 tune is marked by a note value of 1. A note value of 0 means that the
103 current value should not be updated, i.e continue with the previous
104 non-zero value.
105
106 @author Stephen Anthony and Chris D. Walton
107*/
108class CartridgeCTY : public Cartridge
109{
110 friend class CartridgeCTYWidget;
111
112 public:
113 /**
114 Create a new cartridge using the specified image
115
116 @param image Pointer to the ROM image
117 @param size The size of the ROM image
118 @param md5 The md5sum of the ROM image
119 @param settings A reference to the settings object
120 */
121 CartridgeCTY(const ByteBuffer& image, size_t size, const string& md5,
122 const Settings& settings);
123 virtual ~CartridgeCTY() = default;
124
125 public:
126 /**
127 Reset device to its power-on state
128 */
129 void reset() override;
130
131 /**
132 Install cartridge in the specified system. Invoked by the system
133 when the cartridge is attached to it.
134
135 @param system The system the device should install itself in
136 */
137 void install(System& system) override;
138
139 /**
140 Install pages for the specified bank in the system.
141
142 @param bank The bank that should be installed in the system
143 */
144 bool bank(uInt16 bank) override;
145
146 /**
147 Get the current bank.
148
149 @param address The address to use when querying the bank
150 */
151 uInt16 getBank(uInt16 address = 0) const override;
152
153 /**
154 Query the number of banks supported by the cartridge.
155 */
156 uInt16 bankCount() const override;
157
158 /**
159 Patch the cartridge ROM.
160
161 @param address The ROM address to patch
162 @param value The value to place into the address
163 @return Success or failure of the patch operation
164 */
165 bool patch(uInt16 address, uInt8 value) override;
166
167 /**
168 Access the internal ROM image for this cartridge.
169
170 @param size Set to the size of the internal ROM image data
171 @return A pointer to the internal ROM image data
172 */
173 const uInt8* getImage(size_t& size) const override;
174
175 /**
176 Save the current state of this cart to the given Serializer.
177
178 @param out The Serializer object to use
179 @return False on any errors, else true
180 */
181 bool save(Serializer& out) const override;
182
183 /**
184 Load the current state of this cart from the given Serializer.
185
186 @param in The Serializer object to use
187 @return False on any errors, else true
188 */
189 bool load(Serializer& in) override;
190
191 /**
192 Get a descriptor for the device name (used in error checking).
193
194 @return The name of the object
195 */
196 string name() const override { return "CartridgeCTY"; }
197
198 /**
199 Informs the cartridge about the name of the nvram file it will use.
200
201 @param nvramdir The full path of the nvram directory
202 @param romfile The name of the cart from ROM properties
203 */
204 void setNVRamFile(const string& nvramdir, const string& romfile) override;
205
206 #ifdef DEBUGGER_SUPPORT
207 /**
208 Get debugger widget responsible for accessing the inner workings
209 of the cart.
210 */
211 CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
212 const GUI::Font& nfont, int x, int y, int w, int h) override
213 {
214 return new CartridgeCTYWidget(boss, lfont, nfont, x, y, w, h, *this);
215 }
216 #endif
217
218 public:
219 /**
220 Get the byte at the specified address.
221
222 @return The byte at the specified address
223 */
224 uInt8 peek(uInt16 address) override;
225
226 /**
227 Change the byte at the specified address to the given value
228
229 @param address The address where the value should be stored
230 @param value The value to be stored at the address
231 @return True if the poke changed the device address space, else false
232 */
233 bool poke(uInt16 address, uInt8 value) override;
234
235 private:
236 /**
237 Either load or save internal RAM to Harmony EEPROM (represented by
238 a file in emulation).
239
240 @return The value at $FF4 with bit 6 set or cleared (depending on
241 whether the RAM access was busy or successful)
242 */
243 uInt8 ramReadWrite();
244
245 /**
246 Actions initiated by accessing $FF4 hotspot.
247 */
248 void loadTune(uInt8 index);
249 void loadScore(uInt8 index);
250 void saveScore(uInt8 index);
251 void wipeAllScores();
252
253 /**
254 Updates any data fetchers in music mode based on the number of
255 CPU cycles which have passed since the last update.
256 */
257 void updateMusicModeDataFetchers();
258
259 void updateTune();
260
261 private:
262 // The 32K ROM image of the cartridge
263 std::array<uInt8, 32_KB> myImage;
264
265 // The 28K ROM image of the music
266 std::array<uInt8, 28_KB> myTuneData;
267
268 // The 64 bytes of RAM accessible at $1000 - $1080
269 std::array<uInt8, 64> myRAM;
270
271 // Operation type (written to $1000, used by hotspot $1FF4)
272 uInt8 myOperationType;
273
274 // Pointer to the 28K frequency table (points to the start of one
275 // of seven 4K tunes in myTuneData)
276 const uInt8* myFrequencyImage;
277
278 // The counter register for the data fetcher
279 uInt16 myTunePosition;
280
281 // The music mode counters
282 std::array<uInt32, 3> myMusicCounters;
283
284 // The music frequency
285 std::array<uInt32, 3> myMusicFrequencies;
286
287 // Flags that last byte peeked was A9 (LDA #)
288 bool myLDAimmediate;
289
290 // The random number generator register
291 uInt32 myRandomNumber;
292
293 // The time after which the first request of a load/save operation
294 // will actually be completed
295 // Due to Harmony EEPROM constraints, a read/write isn't instantaneous,
296 // so we need to emulate the delay as well
297 uInt64 myRamAccessTimeout;
298
299 // Full pathname of the file to use when emulating load/save
300 // of internal RAM to Harmony cart EEPROM
301 string myEEPROMFile;
302
303 // System cycle count from when the last update to music data fetchers occurred
304 uInt64 myAudioCycles;
305
306 // Fractional DPC music OSC clocks unused during the last update
307 double myFractionalClocks;
308
309 // Indicates the offset into the ROM image (aligns to current bank)
310 uInt16 myBankOffset;
311
312 static const uInt32 ourFrequencyTable[63];
313
314 private:
315 // Following constructors and assignment operators not supported
316 CartridgeCTY() = delete;
317 CartridgeCTY(const CartridgeCTY&) = delete;
318 CartridgeCTY(CartridgeCTY&&) = delete;
319 CartridgeCTY& operator=(const CartridgeCTY&) = delete;
320 CartridgeCTY& operator=(CartridgeCTY&&) = delete;
321};
322
323#endif
324