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 CARTRIDGEDASH_HXX
19#define CARTRIDGEDASH_HXX
20
21class System;
22
23#include "bspf.hxx"
24#include "Cart.hxx"
25
26#ifdef DEBUGGER_SUPPORT
27class CartridgeDASHWidget;
28 #include "CartDASHWidget.hxx"
29#endif
30
31/**
32 Cartridge class for new tiling engine "Boulder Dash" format games with RAM.
33 Kind of a combination of 3F and 3E, with better switchability.
34 B.Watson's Cart3E was used as a template for building this implementation.
35
36 The destination bank (0-3) is held in the top bits of the value written to
37 $3E (for RAM switching) or $3F (for ROM switching). The low 6 bits give
38 the actual bank number (0-63) corresponding to 512 byte blocks for RAM and
39 1024 byte blocks for ROM. The maximum size is therefore 32K RAM and 64K ROM.
40
41 D7D6 indicate the bank number (0-3)
42 D5D4D3D2D1D0 indicate the actual # (0-63) from the image/ram
43
44 ROM:
45
46 Note: in descriptions $F000 is equivalent to $1000 -- that is, we only deal
47 with the low 13 bits of addressing. Stella code uses $1000, I'm used to $F000
48 So, mask with top bits clear :) when reading this document.
49
50 In this scheme, the 4K address space is broken into four 1K ROM segments.
51 living at 0x1000, 0x1400, 0x1800, 0x1C00 (or, same thing, 0xF000... etc.),
52 and four 512 byte RAM segments, living at 0x1000, 0x1200, 0x1400, 0x1600
53 with write-mirrors +0x800 of these. The last 1K ROM ($FC00-$FFFF) segment
54 in the 6502 address space (ie: $1C00-$1FFF) is initialised to point to the
55 FIRST 1K of the ROM image, so the reset vectors must be placed at the
56 end of the first 1K in the ROM image. Note, this is DIFFERENT to 3E which
57 switches in the UPPER bank and this bank is fixed. This allows variable sized
58 ROM without having to detect size. First bank (0) in ROM is the default fixed
59 bank mapped to $FC00.
60
61 The system requires the reset vectors to be valid on a reset, so either the
62 hardware first switches in the first bank, or the programmer must ensure
63 that the reset vector is present in ALL ROM banks which might be switched
64 into the last bank area. Currently the latter (programmer onus) is required,
65 but it would be nice for the cartridge hardware to auto-switch on reset.
66
67 ROM switching (write of block+bank number to $3F) D7D6 upper 2 bits of bank #
68 indicates the destination segment (0-3, corresponding to $F000, $F400, $F800,
69 $FC00), and lower 6 bits indicate the 1K bank to switch in. Can handle 64
70 x 1K ROM banks (64K total).
71
72 D7 D6 D5D4D3D2D1D0
73 0 0 x x x x x x switch a 1K ROM bank xxxxx to $F000
74 0 1 switch a 1K ROM bank xxxxx to $F400
75 1 0 switch a 1K ROM bank xxxxx to $F800
76 1 1 switch a 1K ROM bank xxxxx to $FC00
77
78 RAM switching (write of segment+bank number to $3E) with D7D6 upper 2 bits of
79 bank # indicates the destination RAM segment (0-3, corresponding to $F000,
80 $F200, $F400, $F600). Note that this allows contiguous 2K of RAM to be
81 configured by setting 4 consecutive RAM segments each 512 bytes with
82 consecutive addresses. However, as the write address of RAM is +0x800, this
83 invalidates ROM access as described below.
84
85 can handle 64 x 512 byte RAM banks (32K total)
86
87 D7 D6 D5D4D3D2D1D0
88 0 0 x x x x x x switch a 512 byte RAM bank xxxxx to $F000 with write @ $F800
89 0 1 switch a 512 byte RAM bank xxxxx to $F200 with write @ $FA00
90 1 0 switch a 512 byte RAM bank xxxxx to $F400 with write @ $FC00
91 1 1 switch a 512 byte RAM bank xxxxx to $F600 with write @ $FE00
92
93 It is possible to switch multiple RAM banks and ROM banks together
94
95 For example,
96 F000-F1FF RAM bank A (512 byte READ)
97 F200-F3FF high 512 bytes of ROM bank previously loaded at F000
98 F400 ROM bank 0 (1K)
99 F800 RAM bank A (512 byte WRITE)
100 FA00-FBFF high 512 bytes of ROM bank previously loaded at F400
101 FC00 ROM bank 1
102
103 This example shows 512 bytes of RAM, and 2 1K ROM banks and two 512 byte ROM
104 bank halves.
105
106 Switching RAM blocks (D7D6 of $3E) partially invalidates ROM blocks, as below...
107
108 RAM block Invalidates ROM block
109 0 0 (lower half), 2 (lower half)
110 1 0 (upper half), 2 (upper half)
111 2 1 (lower half), 3 (upper half)
112 3 1 (upper half), 3 (lower half)
113
114 For example, RAM block 1 uses address $F200-$F3FF and $FA00-$FBFF
115 ROM block 0 uses address $F000-$F3FF, and ROM block 2 uses address $F800-$FBFF
116 Switching in RAM block 1 makes F200-F3FF ROM inaccessible, however F000-F1FF is
117 still readable. So, care must be paid.
118
119 This crazy RAM layout is useful as it allows contiguous RAM to be switched in,
120 up to 2K in one sequentially accessible block. This means you CAN have 2K of
121 consecutive RAM (don't forget to copy your reset vectors!)
122
123 @author Andrew Davie
124 */
125
126class CartridgeDASH: public Cartridge
127{
128 friend class CartridgeDASHWidget;
129
130 public:
131 /**
132 Create a new cartridge using the specified image and size
133
134 @param image Pointer to the ROM image
135 @param size The size of the ROM image
136 @param md5 The md5sum of the ROM image
137 @param settings A reference to the various settings (read-only)
138 */
139 CartridgeDASH(const ByteBuffer& image, size_t size, const string& md5,
140 const Settings& settings);
141 virtual ~CartridgeDASH() = default;
142
143 public:
144 /** Reset device to its power-on state */
145 void reset() override;
146
147 /**
148 Install cartridge in the specified system. Invoked by the system
149 when the cartridge is attached to it.
150
151 @param system The system the device should install itself in
152 */
153 void install(System& system) override;
154
155 /**
156 Patch the cartridge ROM.
157
158 @param address The ROM address to patch
159 @param value The value to place into the address
160 @return Success or failure of the patch operation
161 */
162 bool patch(uInt16 address, uInt8 value) override;
163
164 /**
165 Access the internal ROM image for this cartridge.
166
167 @param size Set to the size of the internal ROM image data
168 @return A pointer to the internal ROM image data
169 */
170 const uInt8* getImage(size_t& size) const override;
171
172 /**
173 Save the current state of this cart to the given Serializer.
174
175 @param out The Serializer object to use
176 @return False on any errors, else true
177 */
178 bool save(Serializer& out) const override;
179
180 /**
181 Load the current state of this cart from the given Serializer.
182
183 @param in The Serializer object to use
184 @return False on any errors, else true
185 */
186 bool load(Serializer& in) override;
187
188 /**
189 Get a descriptor for the device name (used in error checking).
190
191 @return The name of the object
192 */
193 string name() const override { return "CartridgeDASH"; }
194
195 #ifdef DEBUGGER_SUPPORT
196 /**
197 Get debugger widget responsible for accessing the inner workings
198 of the cart.
199 */
200 CartDebugWidget* debugWidget(GuiObject* boss, const GUI::Font& lfont,
201 const GUI::Font& nfont, int x, int y, int w, int h) override
202 {
203 return new CartridgeDASHWidget(boss, lfont, nfont, x, y, w, h, *this);
204 }
205 #endif
206
207 public:
208 /**
209 Get the byte at the specified address
210
211 @return The byte at the specified address
212 */
213 uInt8 peek(uInt16 address) override;
214
215 /**
216 Change the byte at the specified address to the given value
217
218 @param address The address where the value should be stored
219 @param value The value to be stored at the address
220 @return True if the poke changed the device address space, else false
221 */
222 bool poke(uInt16 address, uInt8 value) override;
223
224 private:
225 bool bankRAM(uInt8 bank); // switch a RAM bank
226 bool bankROM(uInt8 bank); // switch a ROM bank
227
228 void bankRAMSlot(uInt16 bank); // switch in a 512b RAM slot (lower or upper 1/2 bank)
229 void bankROMSlot(uInt16 bank); // switch in a 512b RAM slot (read or write port)
230
231 void initializeBankState(); // set all banks according to current bankInUse state
232
233 // We have an array that indicates for each of the 8 512 byte areas of the address space, which ROM/RAM
234 // bank is used in that area. ROM switches 1K so occupies 2 successive entries for each switch. RAM occupies
235 // two as well, one 512 byte for read and one for write. The RAM locations are +0x800 apart, and the ROM
236 // are consecutive. This allows us to determine on a read/write exactly where the data is.
237
238 static constexpr uInt16 BANK_UNDEFINED = 0x8000; // bank is undefined and inaccessible
239 std::array<uInt16, 8> bankInUse; // bank being used for ROM/RAM (eight 512 byte areas)
240 std::array<uInt16, 4> segmentInUse; // set by bank methods, to know which hotspot was accessed
241
242 static constexpr uInt16 BANK_SWITCH_HOTSPOT_RAM = 0x3E; // writes to this address cause bankswitching
243 static constexpr uInt16 BANK_SWITCH_HOTSPOT_ROM = 0x3F; // writes to this address cause bankswitching
244
245 static constexpr uInt8 BANK_BITS = 6; // # bits for bank
246 static constexpr uInt8 BIT_BANK_MASK = (1 << BANK_BITS) - 1; // mask for those bits
247 static constexpr uInt16 BITMASK_LOWERUPPER = 0x100; // flags lower or upper section of bank (1==upper)
248 static constexpr uInt16 BITMASK_ROMRAM = 0x200; // flags ROM or RAM bank switching (1==RAM)
249
250 static constexpr uInt16 MAXIMUM_BANK_COUNT = (1 << BANK_BITS);
251 static constexpr uInt16 RAM_BANK_TO_POWER = 9; // 2^n = 512
252 static constexpr uInt16 RAM_BANK_SIZE = (1 << RAM_BANK_TO_POWER);
253 static constexpr uInt16 BITMASK_RAM_BANK = (RAM_BANK_SIZE - 1);
254 static constexpr uInt32 RAM_TOTAL_SIZE = MAXIMUM_BANK_COUNT * RAM_BANK_SIZE;
255
256 static constexpr uInt16 ROM_BANK_TO_POWER = 10; // 2^n = 1024
257 static constexpr uInt16 ROM_BANK_SIZE = (1 << ROM_BANK_TO_POWER);
258 static constexpr uInt16 BITMASK_ROM_BANK = (ROM_BANK_SIZE - 1);
259
260 static constexpr uInt16 ROM_BANK_COUNT = 64;
261
262 static constexpr uInt16 RAM_WRITE_OFFSET = 0x800;
263
264 ByteBuffer myImage; // Pointer to a dynamically allocated ROM image of the cartridge
265 size_t mySize; // Size of the ROM image
266 std::array<uInt8, RAM_TOTAL_SIZE> myRAM;
267
268 private:
269 // Following constructors and assignment operators not supported
270 CartridgeDASH() = delete;
271 CartridgeDASH(const CartridgeDASH&) = delete;
272 CartridgeDASH(CartridgeDASH&&) = delete;
273 CartridgeDASH& operator=(const CartridgeDASH&) = delete;
274 CartridgeDASH& operator=(CartridgeDASH&&) = delete;
275};
276
277#endif
278