OXYGENHUB
◆ main openmsx / src / memory / MegaFlashRomSCCPlusSD.cc
27.6 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "MegaFlashRomSCCPlusSD.hh"
2
3
#include "CheckedRam.hh"
4
#include "SdCard.hh"
5
6
#include "CacheLine.hh"
7
#include "DummyAY8910Periphery.hh"
8
#include "MSXCPUInterface.hh"
9
#include "serialize.hh"
10
11
#include "enumerate.hh"
12
#include "narrow.hh"
13
#include "ranges.hh"
14
#include "xrange.hh"
15
16
#include <array>
17
#include <memory>
18
19
/******************************************************************************
20
 * DOCUMENTATION AS PROVIDED BY MANUEL PAZOS, WHO DEVELOPED THE CARTRIDGE     *
21
 ******************************************************************************
22
23
--------------------------------------------------------------------------------
24
MegaFlashROM SCC+ SD Technical Details
25
(c) Manuel Pazos 24-02-2014
26
--------------------------------------------------------------------------------
27
28
[Main features]
29
30
 - 8192 KB flashROM memory
31
 - SD interface (MegaSD)
32
 - SCC-I (2312P001)
33
 - PSG (AY-3-8910/YM2149)
34
 - Mappers: ASCII8, ASCII16, Konami, Konami SCC, linear 64K
35
 - Slot expander
36
37
38
--------------------------------------------------------------------------------
39
[Memory]
40
41
 - Model Numonyx / Micron M29W640FB / M29W640GB TSOP48
42
 - Datasheet: https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/parallel/m29w/m29w640f.pdf
43
              https://media-www.micron.com/-/media/client/global/documents/products/data-sheet/nor-flash/parallel/m29w/m29w640g.pdf
44
 - Block layout:
45
     #00000 8K x 8
46
     #10000 64K x 127
47
 - Command addresses:
48
     #4555 and #4AAA
49
 - FlashROM ID:
50
    ID_M29W640FB    #FD
51
    ID_M29W640GB    #7E 10 00
52
53
54
--------------------------------------------------------------------------------
55
[PSG]
56
57
 The PSG included in the cartridge is mapped to ports #10-#12
58
59
 Port #A0 -> #10
60
 Port #A1 -> #11
61
 Port #A2 -> #12
62
63
 The PSG is read only.
64
65
--------------------------------------------------------------------------------
66
[Cartridge layout]
67
68
    - Subslot 0: Recovery - 16K linear (Visible on page 1 and 2)
69
    - Subslot 1: MegaFlashROM SCC+ - 7104K (multiple mapper support)
70
    - Subslot 2: RAM (when available)
71
    - Subslot 3: MegaSD - 1024K (ASC8 mapper)
72
73
74
--------------------------------------------------------------------------------
75
[FlashROM layout]
76
77
#000000+----------------+
78
       |    Recovery    | 16K - Subslot 0 (Note: Blocks are write protected
79
by VPP/WD pin)
80
       +----------------+
81
       |   DSK Kernel   | 16K
82
       +----------------+
83
       |    Not used    | 32K
84
#010000+----------------+
85
       |                |
86
       |                |
87
       | MegaFlashROM   | 7104K - Subslot 1
88
       |                |
89
       |                |
90
#700000+----------------+
91
       |                |
92
       |     MegaSD     | 1024K - Subslot 3
93
       |                |
94
       +----------------+
95
96
97
--------------------------------------------------------------------------------
98
[Subslot register (#FFFF)]
99
100
    Available when writing at #FFFF in the cartridge slot.
101
    Reading that address will return all the bits inverted.
102
    Default value = 0
103
104
105
--------------------------------------------------------------------------------
106
[Subslot 0: RECOVERY]
107
108
    Size 16K
109
    Common ROM (Without mapper).
110
    Visible on pages 1 and 2 (mirrored all over the slot)
111
112
113
--------------------------------------------------------------------------------
114
[Subslot 1: MegaFlashROM SCC+ SD]
115
116
  [REGISTERS]
117
118
    [MAPPER REGISTER (#7FFF)]
119
    7	mapper mode 1: \ #00 = SCC,  #40 = 64K
120
    6	mapper mode 0: / #80 = ASC8, #C0 = ASC16
121
    5	mapper mode  :   Select Konami mapper (0=SCC or 1=normal)
122
    4
123
    3	Disable #4000-#5FFF mapper in Konami mode
124
    2	Disable this mapper register #7FFF
125
    1	Disable mapper and offset registers
126
    0	Enable 512K mapper limit in SCC mapper or 256K limit in Konami mapper
127
128
129
    [OFFSET REGISTER (#7FFD)]
130
    7-0 Offset value bits 7-0
131
132
133
    [OFFSET REGISTER (#7FFE)]
134
    1	Offset bit 9
135
    0	Offset bit 8
136
137
138
    [CONFIG REGISTER (#7FFC)]
139
    7	Disable config register (1 = Disabled)
140
    6
141
    5	Disable SRAM (i.e. the RAM in subslot 2)
142
    4	DSK mode (1 = On): Bank 0 and 1 are remapped to DSK kernel (config banks 2-3)
143
    3	Cartridge PSG also mapped to ports #A0-#A3
144
    2	Subslots disabled (1 = Disabled) Only MegaFlashROM SCC+ is available.
145
    1	FlashROM Block protect (1 = Protect) VPP_WD pin
146
    0	FlashROM write enable (1 = Enabled)
147
148
149
150
  [MAPPERS]
151
152
   - ASCII 8:    Common ASC8 mapper
153
154
   - ASCII 16:   Common ASC16 mapper
155
156
   - Konami:     Common Konami mapper.
157
                 Bank0 (#4000-#5FFF) can be also changed unless [MAPPER REGISTER] bit 3 is 1
158
159
   - Konami SCC: Common Konami SCC mapper
160
161
   - Linear 64:  #0000-#3FFF bank0
162
                 #4000-#7FFF bank1
163
                 #8000-#BFFF bank2
164
                 #C000-#FFFF bank3
165
                 Banks mapper registers addresses = Konami
166
167
168
  [DEFAULT VALUES]
169
170
   - MAPPER REGISTER = 0
171
   - CONFIG REGISTER = %00000011
172
   - MapperBank0 = 0
173
   - MapperBank1 = 1
174
   - MapperBank2 = 2
175
   - MapperBank3 = 3
176
   - BankOffset = 0
177
   - Subslot register = 0
178
179
180
  [LOGIC]
181
182
  Bank0   <=    "1111111010" when CONFIG REGISTER(4) = '1' and MapperBank0 = "0000000000" else  ; DSK mode
183
                MapperBank0 + bankOffset;
184
185
  Bank1   <=    "1111111011" when CONFIG REGISTER(4) = '1' and MapperBank1 = "0000000001" else  ; DSK mode
186
                MapperBank1 + bankOffset;
187
188
  Bank2   <=    MapperBank2 + bankOffset;
189
190
  Bank3   <=    MapperBank3 + bankOffset;
191
192
193
  RamAdr  <=
194
                -- Mapper in 64K mode
195
                Bank0(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "00" else  --#0000-#3FFF
196
                Bank1(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "01" else  --#4000-#7FFF
197
                Bank2(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "10" else  --#8000-#BFFF
198
                Bank3(8 downto 0) & adr(13 downto 0) when adr(15 downto 14) = "11" else  --#C000-#FFFF
199
200
                -- Mapper in SCC, ASC8 or ASC16 modes
201
                Bank0(9 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "10" else  --#4000-#5FFF
202
                Bank1(9 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "11" else  --#6000-#7FFF
203
                Bank2(9 downto 0) & adr(12 downto 0) when adr(14 downto 13) = "00" else  --#8000-#9FFF
204
                Bank3(9 downto 0) & adr(12 downto 0);                                    --#A000-#BFFF
205
206
207
  Note: It is possible to access the whole flashROM from the MegaFlashROM SCC+
208
  SD using the offsets register!
209
210
--------------------------------------------------------------------------------
211
[Subslot 2: 512K RAM expansion]
212
213
  Mapper ports:
214
    Page 0 = #FC
215
    Page 1 = #FD
216
    Page 2 = #FE
217
    Page 3 = #FF
218
219
  Default bank values:
220
    Page 0 = 3
221
    Page 1 = 2
222
    Page 2 = 1
223
    Page 3 = 0
224
225
  Disabled when [CONFIG REGISTER] bit 5 = 1
226
227
  Since mapper ports must not be read, as stated on MSX Technical Handbook, and
228
  mapper ports as read only, as stated on MSX Datapack, all read operations on
229
  these ports will not return any value.
230
231
UPDATE:
232
  Initial version indeed did not support reading the memory mapper ports. But
233
  upon user request this feature was added later.
234
235
--------------------------------------------------------------------------------
236
[Subslot 3: MegaSD]
237
238
  Mapper type: ASCII8
239
240
  Default mapper values:
241
    Bank0 = 0
242
    Bank1 = 1
243
    Bank2 = 0
244
    Bank3 = 0
245
246
  Memory range 1024K: Banks #00-#7F are mirrored in #80-#FF (except registers
247
  bank #40-#7F)
248
249
  Memory registers area (Bank #40-#7F):
250
    #4000-#57FF: SD card access (R/W)
251
                 #4000-#4FFF: /CS signal = 0 - SD enabled
252
                 #5000-#5FFF: /CS signal = 1 - SD disabled
253
254
    #5800-#5FFF: SD slot select (bit 0: 0 = SD slot 1, 1 = SD slot 2)
255
256
  Writes to these registers do not write through to the FlashROM.
257
258
  Cards work in SPI mode.
259
  Signals used: CS, DI, DO, SCLK
260
  When reading, 8 bits are read from DO
261
  When writing, 8 bits are written to DI
262
263
  SD specifications: https://www.sdcard.org/downloads/pls/simplified_specs/part1_410.pdf
264
265
******************************************************************************/
266
267
static constexpr unsigned MEMORY_MAPPER_SIZE = 512;
268
static constexpr uint8_t MEMORY_MAPPER_MASK = (MEMORY_MAPPER_SIZE / 16) - 1;
269
270
namespace openmsx {
271
272
MegaFlashRomSCCPlusSD::MegaFlashRomSCCPlusSD(DeviceConfig& config)
273
	: MSXDevice(config)
274
	, flash("MFR SCC+ SD flash", AmdFlashChip::M29W640GB, {}, config)
275
	, scc("MFR SCC+ SD SCC-I", config, getCurrentTime(), SCC::Mode::Compatible)
276
	, psg("MFR SCC+ SD PSG", DummyAY8910Periphery::instance(), config,
277
	      getCurrentTime())
278
	, checkedRam(config.getChildDataAsBool("hasmemorymapper", true) ?
279
		std::make_unique<CheckedRam>(config, getName() + " memory mapper", "memory mapper", MEMORY_MAPPER_SIZE * 1024)
280
		: nullptr)
281
	, mapperIO(checkedRam ? std::make_unique<MapperIO>(*this) : nullptr) // handles ports 0xfc-0xff
282
{
283
	// adjust PSG volume, see details in https://github.com/openMSX/openMSX/issues/1934
284
	// note: this is a theoretical value. The actual relative volume should be measured!
285
	psg.setSoftwareVolume(21000.0f/9000.0f, getCurrentTime());
286
287
	powerUp(getCurrentTime());
288
	getCPUInterface().register_IO_Out_range(0x10, 2, this);
289
290
	sdCard[0] = std::make_unique<SdCard>(DeviceConfig(config, config.findChild("sdcard1")));
291
	sdCard[1] = std::make_unique<SdCard>(DeviceConfig(config, config.findChild("sdcard2")));
292
}
293
294
MegaFlashRomSCCPlusSD::~MegaFlashRomSCCPlusSD()
295
{
296
	// unregister extra PSG I/O ports
297
	updateConfigReg(3);
298
	getCPUInterface().unregister_IO_Out_range(0x10, 2, this);
299
}
300
301
void MegaFlashRomSCCPlusSD::powerUp(EmuTime time)
302
{
303
	scc.powerUp(time);
304
	reset(time);
305
}
306
307
void MegaFlashRomSCCPlusSD::reset(EmuTime time)
308
{
309
	mapperReg = 0;
310
	offsetReg = 0;
311
	updateConfigReg(3);
312
	subslotReg = 0;
313
	ranges::iota(bankRegsSubSlot1, uint16_t(0));
314
315
	sccMode = 0;
316
	ranges::iota(sccBanks, byte(0));
317
	scc.reset(time);
318
319
	psgLatch = 0;
320
	psg.reset(time);
321
322
	flash.reset();
323
324
	// memory mapper
325
	for (auto [i, mr] : enumerate(memMapperRegs)) {
326
		mr = byte(3 - i);
327
	}
328
329
	for (auto [bank, reg] : enumerate(bankRegsSubSlot3)) {
330
		reg = (bank == 1) ? 1 : 0;
331
	}
332
333
	selectedCard = 0;
334
335
	invalidateDeviceRWCache(); // flush all to be sure
336
}
337
338
byte MegaFlashRomSCCPlusSD::getSubSlot(unsigned addr) const
339
{
340
	return isSlotExpanderEnabled() ?
341
		(subslotReg >> (2 * (addr >> 14))) & 3 : 1;
342
}
343
344
void MegaFlashRomSCCPlusSD::writeToFlash(unsigned addr, byte value, EmuTime time)
345
{
346
	if (isFlashRomWriteEnabled()) {
347
		flash.write(addr, value, time);
348
	} else {
349
		// flash is write protected, this is implemented by not passing
350
		// writes to flash at all.
351
	}
352
}
353
354
byte MegaFlashRomSCCPlusSD::peekMem(uint16_t addr, EmuTime time) const
355
{
356
	if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
357
		// read subslot register
358
		return subslotReg ^ 0xFF;
359
	}
360
361
	switch (getSubSlot(addr)) {
362
		case 0: return peekMemSubSlot0(addr, time);
363
		case 1: return peekMemSubSlot1(addr, time);
364
		case 2: return isMemoryMapperEnabled() ?
365
				peekMemSubSlot2(addr) : 0xFF;
366
		case 3: return peekMemSubSlot3(addr, time);
367
		default: UNREACHABLE;
368
	}
369
}
370
371
byte MegaFlashRomSCCPlusSD::readMem(uint16_t addr, EmuTime time)
372
{
373
	if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
374
		// read subslot register
375
		return subslotReg ^ 0xFF;
376
	}
377
378
	switch (getSubSlot(addr)) {
379
		case 0: return readMemSubSlot0(addr, time);
380
		case 1: return readMemSubSlot1(addr, time);
381
		case 2: return isMemoryMapperEnabled() ?
382
				readMemSubSlot2(addr) : 0xFF;
383
		case 3: return readMemSubSlot3(addr, time);
384
		default: UNREACHABLE;
385
	}
386
}
387
388
const byte* MegaFlashRomSCCPlusSD::getReadCacheLine(uint16_t addr) const
389
{
390
	if (isSlotExpanderEnabled() &&
391
		((addr & CacheLine::HIGH) == (0xFFFF & CacheLine::HIGH))) {
392
		// read subslot register
393
		return nullptr;
394
	}
395
396
	switch (getSubSlot(addr)) {
397
		case 0: return getReadCacheLineSubSlot0(addr);
398
		case 1: return getReadCacheLineSubSlot1(addr);
399
		case 2: return isMemoryMapperEnabled() ?
400
				getReadCacheLineSubSlot2(addr) : unmappedRead.data();
401
		case 3: return getReadCacheLineSubSlot3(addr);
402
		default: UNREACHABLE;
403
	}
404
}
405
406
void MegaFlashRomSCCPlusSD::writeMem(uint16_t addr, byte value, EmuTime time)
407
{
408
	if (isSlotExpanderEnabled() && (addr == 0xFFFF)) {
409
		// write subslot register
410
		byte diff = value ^ subslotReg;
411
		subslotReg = value;
412
		for (auto i : xrange(4)) {
413
			if (diff & (3 << (2 * i))) {
414
				invalidateDeviceRWCache(0x4000 * i, 0x4000);
415
			}
416
		}
417
	}
418
419
	switch (getSubSlot(addr)) {
420
		case 0: writeMemSubSlot0(addr, value, time); break;
421
		case 1: writeMemSubSlot1(addr, value, time); break;
422
		case 2: if (isMemoryMapperEnabled()) {
423
				writeMemSubSlot2(addr, value);
424
			}
425
			break;
426
		case 3: writeMemSubSlot3(addr, value, time); break;
427
		default: UNREACHABLE;
428
	}
429
}
430
431
byte* MegaFlashRomSCCPlusSD::getWriteCacheLine(uint16_t addr)
432
{
433
	if (isSlotExpanderEnabled() &&
434
		((addr & CacheLine::HIGH) == (0xFFFF & CacheLine::HIGH))) {
435
		// read subslot register
436
		return nullptr;
437
	}
438
439
	switch (getSubSlot(addr)) {
440
		case 0: return getWriteCacheLineSubSlot0(addr);
441
		case 1: return getWriteCacheLineSubSlot1(addr);
442
		case 2: return isMemoryMapperEnabled() ?
443
				getWriteCacheLineSubSlot2(addr) : unmappedWrite.data();
444
		case 3: return getWriteCacheLineSubSlot3(addr);
445
		default: UNREACHABLE;
446
	}
447
}
448
449
/////////////////////// sub slot 0 ////////////////////////////////////////////
450
451
byte MegaFlashRomSCCPlusSD::readMemSubSlot0(uint16_t addr, EmuTime time)
452
{
453
	// read from the first 16kB of flash
454
	// Pazos: ROM and flash can be accessed in all pages (0,1,2,3) (#0000-#FFFF)
455
	return flash.read(addr & 0x3FFF, time);
456
}
457
458
byte MegaFlashRomSCCPlusSD::peekMemSubSlot0(uint16_t addr, EmuTime time) const
459
{
460
	// read from the first 16kB of flash
461
	// Pazos: ROM and flash can be accessed in all pages (0,1,2,3) (#0000-#FFFF)
462
	return flash.peek(addr & 0x3FFF, time);
463
}
464
465
const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot0(uint16_t addr) const
466
{
467
	return flash.getReadCacheLine(addr & 0x3FFF);
468
}
469
470
void MegaFlashRomSCCPlusSD::writeMemSubSlot0(uint16_t addr, byte value, EmuTime time)
471
{
472
	// Pazos: ROM and flash can be accessed in all pages (0,1,2,3) (#0000-#FFFF)
473
	writeToFlash(addr & 0x3FFF, value, time);
474
}
475
476
byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot0(uint16_t /*addr*/)
477
{
478
	return nullptr; // flash isn't cacheable
479
}
480
481
/////////////////////// sub slot 1 ////////////////////////////////////////////
482
483
void MegaFlashRomSCCPlusSD::updateConfigReg(byte value)
484
{
485
	if ((value ^ configReg) & 0x08) {
486
		if (value & 0x08) {
487
			for (auto port : {0xa0, 0xa1}) {
488
				getCPUInterface().register_IO_Out(narrow_cast<byte>(port), this);
489
			}
490
		} else {
491
			for (auto port : {0xa0, 0xa1}) {
492
				getCPUInterface().unregister_IO_Out(narrow_cast<byte>(port), this);
493
			}
494
		}
495
	}
496
	configReg = value;
497
	flash.setVppWpPinLow(isFlashRomBlockProtectEnabled());
498
	invalidateDeviceRWCache(); // flush all to be sure
499
}
500
501
MegaFlashRomSCCPlusSD::SCCEnable MegaFlashRomSCCPlusSD::getSCCEnable() const
502
{
503
	if ((sccMode & 0x20) && (sccBanks[3] & 0x80)) {
504
		return EN_SCCPLUS;
505
	} else if ((!(sccMode & 0x20)) && ((sccBanks[2] & 0x3F) == 0x3F)) {
506
		return EN_SCC;
507
	} else {
508
		return EN_NONE;
509
	}
510
}
511
512
unsigned MegaFlashRomSCCPlusSD::getFlashAddrSubSlot1(unsigned addr) const
513
{
514
	unsigned page = is64KmapperConfigured() ? (addr >> 14) : ((addr >> 13) - 2);
515
	unsigned size = is64KmapperConfigured() ? 0x4000 : 0x2000;
516
517
	if (page >= 4) return unsigned(-1); // outside [0x4000, 0xBFFF] for non-64K mapper
518
519
	unsigned bank = bankRegsSubSlot1[page];
520
	if        (isDSKmodeEnabled() && (page == 0) && (bank == 0)) {
521
		bank = 0x3FA;
522
	} else if (isDSKmodeEnabled() && (page == 1) && (bank == 1)) {
523
		bank = 0x3FB;
524
	} else { // not DSK mode
525
		bank += offsetReg;
526
	}
527
528
	unsigned tmp = (bank * size) + (addr & (size - 1));
529
	return (tmp +  0x010000) & 0x7FFFFF; // wrap at 8MB
530
}
531
532
byte MegaFlashRomSCCPlusSD::readMemSubSlot1(uint16_t addr, EmuTime time)
533
{
534
	if (isKonamiSCCmapperConfigured()) { // Konami SCC
535
		SCCEnable enable = getSCCEnable();
536
		if (((enable == EN_SCC)     && (0x9800 <= addr) && (addr < 0xA000)) ||
537
		    ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
538
			byte val = scc.readMem(narrow_cast<uint8_t>(addr & 0xFF), time);
539
			return val;
540
		}
541
	}
542
543
	unsigned flashAddr = getFlashAddrSubSlot1(addr);
544
	return (flashAddr != unsigned(-1))
545
		? flash.read(flashAddr, time)
546
		: 0xFF; // unmapped read
547
}
548
549
byte MegaFlashRomSCCPlusSD::peekMemSubSlot1(uint16_t addr, EmuTime time) const
550
{
551
	if (isKonamiSCCmapperConfigured()) { // Konami SCC
552
		SCCEnable enable = getSCCEnable();
553
		if (((enable == EN_SCC)     && (0x9800 <= addr) && (addr < 0xA000)) ||
554
		    ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
555
			byte val = scc.peekMem(narrow_cast<uint8_t>(addr & 0xFF), time);
556
			return val;
557
		}
558
	}
559
560
	unsigned flashAddr = getFlashAddrSubSlot1(addr);
561
	return (flashAddr != unsigned(-1))
562
		? flash.peek(flashAddr, time)
563
		: 0xFF; // unmapped read
564
}
565
566
const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot1(uint16_t addr) const
567
{
568
	if (isKonamiSCCmapperConfigured()) {
569
		SCCEnable enable = getSCCEnable();
570
		if (((enable == EN_SCC)     && (0x9800 <= addr) && (addr < 0xA000)) ||
571
		    ((enable == EN_SCCPLUS) && (0xB800 <= addr) && (addr < 0xC000))) {
572
			return nullptr;
573
		}
574
	}
575
576
	unsigned flashAddr = getFlashAddrSubSlot1(addr);
577
	return (flashAddr != unsigned(-1))
578
		? flash.getReadCacheLine(flashAddr)
579
		: unmappedRead.data();
580
}
581
582
void MegaFlashRomSCCPlusSD::writeMemSubSlot1(uint16_t addr, byte value, EmuTime time)
583
{
584
	// address is calculated before writes to other regions take effect
585
	unsigned flashAddr = getFlashAddrSubSlot1(addr);
586
587
	// There are several overlapping functional regions in the address
588
	// space. A single write can trigger behaviour in multiple regions. In
589
	// other words there's no priority amongst the regions where a higher
590
	// priority region blocks the write from the lower priority regions.
591
	// This only goes for places where the flash is 'seen', so not for the
592
	// SCC registers and the SSR
593
594
	if (!isConfigRegDisabled() && (addr == 0x7FFC)) {
595
		// write config register
596
		updateConfigReg(value);
597
	}
598
599
	if (!isMapperRegisterDisabled() && (addr == 0x7FFF)) {
600
		// write mapper register
601
		mapperReg = value;
602
		invalidateDeviceRWCache(); // flush all to be sure
603
	}
604
605
	if (!areBankRegsAndOffsetRegsDisabled() && (addr == 0x7FFD)) {
606
		// write offset register low part
607
		offsetReg = (offsetReg & 0x300) | value;
608
		invalidateDeviceRWCache();
609
	}
610
611
	if (!areBankRegsAndOffsetRegsDisabled() && (addr == 0x7FFE)) {
612
		// write offset register high part (bit 8 and 9)
613
		offsetReg = (offsetReg & 0xFF) + ((value & 0x3) << 8);
614
		invalidateDeviceRWCache();
615
	}
616
617
	if (isKonamiSCCmapperConfigured()) {
618
		// Konami-SCC
619
		if ((addr & 0xFFFE) == 0xBFFE) {
620
			sccMode = value;
621
			scc.setMode((value & 0x20) ? SCC::Mode::Plus
622
			                           : SCC::Mode::Compatible);
623
			invalidateDeviceRWCache(0x9800, 0x800);
624
			invalidateDeviceRWCache(0xB800, 0x800);
625
		}
626
		SCCEnable enable = getSCCEnable();
627
		bool isRamSegment2 = ((sccMode & 0x24) == 0x24) ||
628
		                     ((sccMode & 0x10) == 0x10);
629
		bool isRamSegment3 = ((sccMode & 0x10) == 0x10);
630
		if (((enable == EN_SCC)     && !isRamSegment2 &&
631
		     (0x9800 <= addr) && (addr < 0xA000)) ||
632
		    ((enable == EN_SCCPLUS) && !isRamSegment3 &&
633
		     (0xB800 <= addr) && (addr < 0xC000))) {
634
			scc.writeMem(narrow_cast<uint8_t>(addr & 0xFF), value, time);
635
			return; // Pazos: when SCC registers are selected flashROM is not seen, so it does not accept commands.
636
		}
637
	}
638
639
	unsigned page8kB = (addr >> 13) - 2;
640
	if (!areBankRegsAndOffsetRegsDisabled() && (page8kB < 4)) {
641
		// (possibly) write to bank registers
642
		switch (mapperReg & 0xE0) {
643
		case 0x00:
644
			// Konami-SCC
645
			if ((addr & 0x1800) == 0x1000) {
646
				// Storing 'sccBanks' may seem redundant at
647
				// first, but it's required to calculate
648
				// whether the SCC is enabled or not.
649
				sccBanks[page8kB] = value;
650
				// Masking of the mapper bits is done on
651
				// write (and only in Konami(-scc) mode)
652
				byte mask = areKonamiMapperLimitsEnabled() ? 0x3F : 0xFF;
653
				bankRegsSubSlot1[page8kB] = value & mask;
654
				invalidateDeviceRWCache(0x4000 + 0x2000 * page8kB, 0x2000);
655
			}
656
			break;
657
		case 0x20: {
658
			// Konami
659
			if (isWritingKonamiBankRegisterDisabled() && (addr < 0x6000)) {
660
				// Switching 0x4000-0x5FFF disabled.
661
				// This bit blocks writing to the bank register
662
				// (an alternative was forcing a 0 on read).
663
				// It only has effect in Konami mode.
664
				break;
665
			}
666
			// Making of the mapper bits is done on
667
			// write (and only in Konami(-scc) mode)
668
			if ((addr < 0x5000) || ((0x5800 <= addr) && (addr < 0x6000))) break; // only SCC range works
669
			byte mask = areKonamiMapperLimitsEnabled() ? 0x1F : 0xFF;
670
			bankRegsSubSlot1[page8kB] = value & mask;
671
			invalidateDeviceRWCache(0x4000 + 0x2000 * page8kB, 0x2000);
672
			break;
673
		}
674
		case 0x40:
675
		case 0x60:
676
			// 64kB
677
			bankRegsSubSlot1[page8kB] = value;
678
			invalidateDeviceRWCache(0x0000 + 0x4000 * page8kB, 0x4000);
679
			break;
680
		case 0x80:
681
		case 0xA0:
682
			// ASCII-8
683
			if ((0x6000 <= addr) && (addr < 0x8000)) {
684
				byte bank = (addr >> 11) & 0x03;
685
				bankRegsSubSlot1[bank] = value;
686
				invalidateDeviceRWCache(0x4000 + 0x2000 * bank, 0x2000);
687
			}
688
			break;
689
		case 0xC0:
690
		case 0xE0:
691
			// ASCII-16
692
			// This behaviour is confirmed by Manuel Pazos (creator
693
			// of the cartridge): ASCII-16 uses all 4 bank registers
694
			// and one bank switch changes 2 registers at once.
695
			// This matters when switching mapper mode, because
696
			// the content of the bank registers is unchanged after
697
			// a switch.
698
			// In the first versions of the VHDL code, the bankRegs
699
			// were 8-bit, but due to the construction explained
700
			// above, this results in a maximum size of 2MB for the
701
			// ASCII-16 mapper, as we throw away 1 bit. Later,
702
			// Manuel Pazos made an update to use 9 bits for the
703
			// registers to overcome this limitation. We emulate
704
			// the updated version now.
705
			const uint16_t mask = (1 << 9) - 1;
706
			if ((0x6000 <= addr) && (addr < 0x6800)) {
707
				bankRegsSubSlot1[0] = (2 * value + 0) & mask;
708
				bankRegsSubSlot1[1] = (2 * value + 1) & mask;
709
				invalidateDeviceRWCache(0x4000, 0x4000);
710
			}
711
			if ((0x7000 <= addr) && (addr < 0x7800)) {
712
				bankRegsSubSlot1[2] = (2 * value + 0) & mask;
713
				bankRegsSubSlot1[3] = (2 * value + 1) & mask;
714
				invalidateDeviceRWCache(0x8000, 0x4000);
715
			}
716
			break;
717
		}
718
	}
719
720
	if (flashAddr != unsigned(-1)) {
721
		writeToFlash(flashAddr, value, time);
722
	}
723
}
724
725
byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot1(uint16_t /*addr*/)
726
{
727
	return nullptr; // flash isn't cacheable
728
}
729
730
/////////////////////// sub slot 2 ////////////////////////////////////////////
731
732
unsigned MegaFlashRomSCCPlusSD::calcMemMapperAddress(uint16_t address) const
733
{
734
	auto bank = memMapperRegs[address >> 14];
735
	return ((bank & MEMORY_MAPPER_MASK) << 14) | (address & 0x3FFF);
736
}
737
738
byte MegaFlashRomSCCPlusSD::readMemSubSlot2(uint16_t addr)
739
{
740
	// read from the memory mapper
741
	return checkedRam->read(calcMemMapperAddress(addr));
742
}
743
744
byte MegaFlashRomSCCPlusSD::peekMemSubSlot2(uint16_t addr) const
745
{
746
	return checkedRam->peek(calcMemMapperAddress(addr));
747
}
748
749
const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot2(uint16_t addr) const
750
{
751
	return checkedRam->getReadCacheLine(calcMemMapperAddress(addr));
752
}
753
754
void MegaFlashRomSCCPlusSD::writeMemSubSlot2(uint16_t addr, byte value)
755
{
756
	// write to the memory mapper
757
	checkedRam->write(calcMemMapperAddress(addr), value);
758
}
759
760
byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot2(uint16_t addr)
761
{
762
	return checkedRam->getWriteCacheLine(calcMemMapperAddress(addr));
763
}
764
765
byte MegaFlashRomSCCPlusSD::MapperIO::readIO(uint16_t port, EmuTime time)
766
{
767
	return peekIO(port, time);
768
}
769
770
byte MegaFlashRomSCCPlusSD::MapperIO::peekIO(uint16_t port, EmuTime /*time*/) const
771
{
772
	return getSelectedSegment(port & 3) | byte(~MEMORY_MAPPER_MASK);
773
}
774
775
void MegaFlashRomSCCPlusSD::MapperIO::writeIO(uint16_t port, byte value, EmuTime /*time*/)
776
{
777
	mega.memMapperRegs[port & 3] = value & MEMORY_MAPPER_MASK;
778
	mega.invalidateDeviceRWCache(0x4000 * (port & 0x03), 0x4000);
779
}
780
781
byte MegaFlashRomSCCPlusSD::MapperIO::getSelectedSegment(byte page) const
782
{
783
	return mega.memMapperRegs[page];
784
}
785
786
/////////////////////// sub slot 3 ////////////////////////////////////////////
787
788
unsigned MegaFlashRomSCCPlusSD::getFlashAddrSubSlot3(unsigned addr) const
789
{
790
	unsigned page8kB = (addr >> 13) - 2;
791
	return (bankRegsSubSlot3[page8kB] & 0x7f) * 0x2000 + (addr & 0x1fff) + 0x700000;
792
}
793
794
byte MegaFlashRomSCCPlusSD::readMemSubSlot3(uint16_t addr, EmuTime time)
795
{
796
	if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
797
		// transfer from SD card
798
		return sdCard[selectedCard]->transfer(0xFF, (addr & 0x1000) != 0);
799
	}
800
801
	if ((0x4000 <= addr) && (addr < 0xC000)) {
802
		// read (flash)rom content
803
		unsigned flashAddr = getFlashAddrSubSlot3(addr);
804
		return flash.read(flashAddr, time);
805
	} else {
806
		// unmapped read
807
		return 0xFF;
808
	}
809
}
810
811
byte MegaFlashRomSCCPlusSD::peekMemSubSlot3(uint16_t addr, EmuTime time) const
812
{
813
	if ((0x4000 <= addr) && (addr < 0xC000)) {
814
		// read (flash)rom content
815
		unsigned flashAddr = getFlashAddrSubSlot3(addr);
816
		return flash.peek(flashAddr, time);
817
	} else {
818
		// unmapped read
819
		return 0xFF;
820
	}
821
}
822
823
const byte* MegaFlashRomSCCPlusSD::getReadCacheLineSubSlot3(uint16_t addr) const
824
{
825
	if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
826
		return nullptr;
827
	}
828
829
	if ((0x4000 <= addr) && (addr < 0xC000)) {
830
		// (flash)rom content
831
		unsigned flashAddr = getFlashAddrSubSlot3(addr);
832
		return flash.getReadCacheLine(flashAddr);
833
	} else {
834
		return unmappedRead.data();
835
	}
836
}
837
838
void MegaFlashRomSCCPlusSD::writeMemSubSlot3(uint16_t addr, byte value, EmuTime time)
839
{
840
841
	if (((bankRegsSubSlot3[0] & 0xC0) == 0x40) && ((0x4000 <= addr) && (addr < 0x6000))) {
842
		if (addr >= 0x5800) {
843
			selectedCard = value & 1;
844
		} else {
845
			// transfer to SD card
846
			sdCard[selectedCard]->transfer(value, (addr & 0x1000) != 0); // ignore return value
847
		}
848
	} else {
849
		// write to flash (first, before modifying bank regs)
850
		if ((0x4000 <= addr) && (addr < 0xC000)) {
851
			unsigned flashAddr = getFlashAddrSubSlot3(addr);
852
			writeToFlash(flashAddr, value, time);
853
		}
854
855
		// ASCII-8 mapper
856
		if ((0x6000 <= addr) && (addr < 0x8000)) {
857
			byte page8kB = (addr >> 11) & 0x03;
858
			bankRegsSubSlot3[page8kB] = value;
859
			invalidateDeviceRWCache(0x4000 + 0x2000 * page8kB, 0x2000);
860
		}
861
	}
862
}
863
864
byte* MegaFlashRomSCCPlusSD::getWriteCacheLineSubSlot3(uint16_t /*addr*/)
865
{
866
	return nullptr; // flash isn't cacheable
867
}
868
869
/////////////////////// I/O ////////////////////////////////////////////
870
871
void MegaFlashRomSCCPlusSD::writeIO(uint16_t port, byte value, EmuTime time)
872
{
873
	switch (port & 0xFF) {
874
		case 0xA0:
875
			if (!isPSGalsoMappedToNormalPorts()) return;
876
			[[fallthrough]];
877
		case 0x10:
878
			psgLatch = value & 0x0F;
879
			break;
880
881
		case 0xA1:
882
			if (!isPSGalsoMappedToNormalPorts()) return;
883
			[[fallthrough]];
884
		case 0x11:
885
			psg.writeRegister(psgLatch, value, time);
886
			break;
887
888
		default:
889
			UNREACHABLE;
890
	}
891
}
892
893
template<typename Archive>
894
void MegaFlashRomSCCPlusSD::serialize(Archive& ar, unsigned /*version*/)
895
{
896
	// skip MSXRom base class
897
	ar.template serializeBase<MSXDevice>(*this);
898
899
	// overall
900
	ar.serialize("flash",      flash,
901
	             "subslotReg", subslotReg);
902
903
	// subslot 0 stuff
904
	// (nothing)
905
906
	// subslot 1 stuff
907
	ar.serialize("scc",       scc,
908
	             "sccMode",   sccMode,
909
	             "sccBanks",  sccBanks,
910
	             "psg",       psg,
911
	             "psgLatch",  psgLatch,
912
	             "configReg", configReg,
913
	             "mapperReg", mapperReg,
914
	             "offsetReg", offsetReg,
915
	             "bankRegsSubSlot1", bankRegsSubSlot1);
916
	if constexpr (Archive::IS_LOADER) {
917
		// Re-register PSG ports (if needed)
918
		byte tmp = configReg;
919
		configReg = 3; // set to un-registered
920
		updateConfigReg(tmp); // restore correct value
921
	}
922
923
	// subslot 2 stuff
924
	// TODO ar.serialize("checkedRam", checkedRam);
925
	if (checkedRam) ar.serialize("ram", checkedRam->getUncheckedRam());
926
	ar.serialize("memMapperRegs", memMapperRegs);
927
928
	// subslot 3 stuff
929
	ar.serialize("bankRegsSubSlot3", bankRegsSubSlot3,
930
	             "selectedCard",     selectedCard,
931
	             "sdCard0",          *sdCard[0],
932
	             "sdCard1",          *sdCard[1]);
933
}
934
INSTANTIATE_SERIALIZE_METHODS(MegaFlashRomSCCPlusSD);
935
REGISTER_MSXDEVICE(MegaFlashRomSCCPlusSD, "MegaFlashRomSCCPlusSD");
936
937
} // namespace openmsx