Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
folium-app
GitHub Repository: folium-app/Folium
Path: blob/a-new-beginning/Cherry/Core/GearcolecoCore.cpp
2 views
1
/*
2
* Gearcoleco - ColecoVision Emulator
3
* Copyright (C) 2021 Ignacio Sanchez
4
5
* This program is free software: you can redistribute it and/or modify
6
* it under the terms of the GNU General Public License as published by
7
* the Free Software Foundation, either version 3 of the License, or
8
* any later version.
9
10
* This program is distributed in the hope that it will be useful,
11
* but WITHOUT ANY WARRANTY; without even the implied warranty of
12
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
* GNU General Public License for more details.
14
15
* You should have received a copy of the GNU General Public License
16
* along with this program. If not, see http://www.gnu.org/licenses/
17
*
18
*/
19
20
#include <iomanip>
21
#include "GearcolecoCore.h"
22
#include "CVMemory.h"
23
#include "Processor.h"
24
#include "Audio.h"
25
#include "Video.h"
26
#include "Input.h"
27
#include "Cartridge.h"
28
#include "ColecoVisionIOPorts.h"
29
#include "no_bios.h"
30
#include "common.h"
31
32
GearcolecoCore::GearcolecoCore()
33
{
34
InitPointer(m_pMemory);
35
InitPointer(m_pProcessor);
36
InitPointer(m_pAudio);
37
InitPointer(m_pVideo);
38
InitPointer(m_pInput);
39
InitPointer(m_pCartridge);
40
InitPointer(m_pColecoVisionIOPorts);
41
m_bPaused = true;
42
m_pixelFormat = GC_PIXEL_RGB888;
43
}
44
45
GearcolecoCore::~GearcolecoCore()
46
{
47
SafeDelete(m_pColecoVisionIOPorts);
48
SafeDelete(m_pCartridge);
49
SafeDelete(m_pInput);
50
SafeDelete(m_pVideo);
51
SafeDelete(m_pAudio);
52
SafeDelete(m_pProcessor);
53
SafeDelete(m_pMemory);
54
}
55
56
void GearcolecoCore::Init(GC_Color_Format pixelFormat)
57
{
58
Log("Loading %s core %s by Ignacio Sanchez", GEARCOLECO_TITLE, GEARCOLECO_VERSION);
59
60
m_pixelFormat = pixelFormat;
61
62
m_pCartridge = new Cartridge();
63
m_pMemory = new CVMemory(m_pCartridge);
64
m_pProcessor = new Processor(m_pMemory);
65
m_pAudio = new Audio();
66
m_pVideo = new Video(m_pMemory, m_pProcessor);
67
m_pInput = new Input(m_pProcessor);
68
m_pColecoVisionIOPorts = new ColecoVisionIOPorts(m_pAudio, m_pVideo, m_pInput, m_pCartridge, m_pMemory, m_pProcessor);
69
70
m_pMemory->Init();
71
m_pProcessor->Init();
72
m_pAudio->Init();
73
m_pVideo->Init();
74
m_pInput->Init();
75
m_pCartridge->Init();
76
77
m_pProcessor->SetIOPOrts(m_pColecoVisionIOPorts);
78
}
79
80
bool GearcolecoCore::RunToVBlank(u8* pFrameBuffer, s16* pSampleBuffer, int* pSampleCount, bool step, bool stopOnBreakpoints)
81
{
82
if (!m_pMemory->IsBiosLoaded())
83
{
84
RenderFrameBuffer(pFrameBuffer);
85
return false;
86
}
87
88
bool breakpoint = false;
89
90
if (!m_bPaused && m_pCartridge->IsReady())
91
{
92
bool vblank = false;
93
int totalClocks = 0;
94
while (!vblank)
95
{
96
#ifdef PERFORMANCE
97
unsigned int clockCycles = m_pProcessor->RunFor(75);
98
#else
99
unsigned int clockCycles = m_pProcessor->RunFor(1);
100
#endif
101
vblank = m_pVideo->Tick(clockCycles);
102
m_pAudio->Tick(clockCycles);
103
m_pMemory->Tick(clockCycles);
104
105
totalClocks += clockCycles;
106
107
#ifndef GEARCOLECO_DISABLE_DISASSEMBLER
108
if ((step || (stopOnBreakpoints && m_pProcessor->BreakpointHit())) && !m_pProcessor->DuringInputOpcode())
109
{
110
vblank = true;
111
if (m_pProcessor->BreakpointHit())
112
breakpoint = true;
113
}
114
#endif
115
116
if (totalClocks > 702240)
117
vblank = true;
118
}
119
120
m_pAudio->EndFrame(pSampleBuffer, pSampleCount);
121
RenderFrameBuffer(pFrameBuffer);
122
}
123
124
return breakpoint;
125
}
126
127
bool GearcolecoCore::LoadROM(const char* szFilePath, Cartridge::ForceConfiguration* config)
128
{
129
if (m_pCartridge->LoadFromFile(szFilePath))
130
{
131
if (IsValidPointer(config))
132
m_pCartridge->ForceConfig(*config);
133
134
m_pMemory->SetupMapper();
135
Reset();
136
137
m_pMemory->ResetRomDisassembledMemory();
138
m_pProcessor->DisassembleNextOpcode();
139
140
return true;
141
}
142
else
143
return false;
144
}
145
146
bool GearcolecoCore::LoadROMFromBuffer(const u8* buffer, int size, Cartridge::ForceConfiguration* config)
147
{
148
if (m_pCartridge->LoadFromBuffer(buffer, size))
149
{
150
if (IsValidPointer(config))
151
m_pCartridge->ForceConfig(*config);
152
153
m_pMemory->SetupMapper();
154
Reset();
155
156
m_pMemory->ResetRomDisassembledMemory();
157
m_pProcessor->DisassembleNextOpcode();
158
159
return true;
160
}
161
else
162
return false;
163
}
164
165
void GearcolecoCore::SaveDisassembledROM()
166
{
167
CVMemory::stDisassembleRecord** biosMap = m_pMemory->GetDisassembledBiosMemoryMap();
168
CVMemory::stDisassembleRecord** romMap = m_pMemory->GetDisassembledRomMemoryMap();
169
170
if (m_pCartridge->IsReady() && (strlen(m_pCartridge->GetFilePath()) > 0) && IsValidPointer(romMap))
171
{
172
using namespace std;
173
174
char path[512];
175
176
strcpy(path, m_pCartridge->GetFilePath());
177
strcat(path, ".dis");
178
179
Log("Saving Disassembled ROM %s...", path);
180
181
ofstream myfile;
182
open_ofstream_utf8(myfile, path, ios::out | ios::trunc);
183
184
if (myfile.is_open())
185
{
186
#define PAD_ADDR(digits) std::uppercase << std::hex << std::setw(digits) << std::setfill('0')
187
#define PAD_MEM(chars) std::setw(chars) << std::setfill(' ')
188
189
for (int i = 0; i < 0x2000; i++)
190
{
191
if (IsValidPointer(biosMap[i]) && (biosMap[i]->name[0] != 0))
192
{
193
myfile << "BIOS $" << PAD_ADDR(4) << i << " " << PAD_MEM(12) << biosMap[i]->bytes << " " << biosMap[i]->name << "\n";
194
}
195
}
196
197
for (int i = 0; i < MAX_ROM_SIZE; i++)
198
{
199
if (IsValidPointer(romMap[i]) && (romMap[i]->name[0] != 0))
200
{
201
myfile << "ROM $" << PAD_ADDR(4) << i + 0x8000 << " " << PAD_MEM(12) << romMap[i]->bytes << " " << romMap[i]->name << "\n";
202
}
203
}
204
205
myfile.close();
206
}
207
208
Debug("Disassembled ROM Saved");
209
}
210
}
211
212
bool GearcolecoCore::GetRuntimeInfo(GC_RuntimeInfo& runtime_info)
213
{
214
runtime_info.screen_width = GC_RESOLUTION_WIDTH;
215
runtime_info.screen_height = GC_RESOLUTION_HEIGHT;
216
runtime_info.region = Region_NTSC;
217
218
if (m_pCartridge->IsReady() && m_pMemory->IsBiosLoaded())
219
{
220
if (m_pVideo->GetOverscan() == Video::OverscanFull284)
221
runtime_info.screen_width = GC_RESOLUTION_WIDTH + GC_RESOLUTION_SMS_OVERSCAN_H_284_L + GC_RESOLUTION_SMS_OVERSCAN_H_284_R;
222
if (m_pVideo->GetOverscan() == Video::OverscanFull320)
223
runtime_info.screen_width = GC_RESOLUTION_WIDTH + GC_RESOLUTION_SMS_OVERSCAN_H_320_L + GC_RESOLUTION_SMS_OVERSCAN_H_320_R;
224
if (m_pVideo->GetOverscan() != Video::OverscanDisabled)
225
runtime_info.screen_height = GC_RESOLUTION_HEIGHT + (2 * (m_pCartridge->IsPAL() ? GC_RESOLUTION_OVERSCAN_V_PAL : GC_RESOLUTION_OVERSCAN_V));
226
runtime_info.region = m_pCartridge->IsPAL() ? Region_PAL : Region_NTSC;
227
return true;
228
}
229
230
return false;
231
}
232
233
CVMemory* GearcolecoCore::GetMemory()
234
{
235
return m_pMemory;
236
}
237
238
Cartridge* GearcolecoCore::GetCartridge()
239
{
240
return m_pCartridge;
241
}
242
243
Processor* GearcolecoCore::GetProcessor()
244
{
245
return m_pProcessor;
246
}
247
248
Audio* GearcolecoCore::GetAudio()
249
{
250
return m_pAudio;
251
}
252
253
Video* GearcolecoCore::GetVideo()
254
{
255
return m_pVideo;
256
}
257
258
void GearcolecoCore::KeyPressed(GC_Controllers controller, GC_Keys key)
259
{
260
m_pInput->KeyPressed(controller, key);
261
}
262
263
void GearcolecoCore::KeyReleased(GC_Controllers controller, GC_Keys key)
264
{
265
m_pInput->KeyReleased(controller, key);
266
}
267
268
void GearcolecoCore::Spinner1(int movement)
269
{
270
m_pInput->Spinner1(movement);
271
}
272
273
void GearcolecoCore::Spinner2(int movement)
274
{
275
m_pInput->Spinner2(movement);
276
}
277
278
void GearcolecoCore::Pause(bool paused)
279
{
280
if (paused)
281
{
282
Log("Gearcoleco PAUSED");
283
}
284
else
285
{
286
Log("Gearcoleco RESUMED");
287
}
288
m_bPaused = paused;
289
}
290
291
bool GearcolecoCore::IsPaused()
292
{
293
return m_bPaused;
294
}
295
296
void GearcolecoCore::ResetROM(Cartridge::ForceConfiguration* config)
297
{
298
if (m_pCartridge->IsReady())
299
{
300
Log("Gearcoleco RESET");
301
302
if (IsValidPointer(config))
303
m_pCartridge->ForceConfig(*config);
304
305
Reset();
306
307
m_pProcessor->DisassembleNextOpcode();
308
}
309
}
310
311
void GearcolecoCore::ResetROMPreservingRAM(Cartridge::ForceConfiguration* config)
312
{
313
// TODO
314
315
ResetROM(config);
316
}
317
318
void GearcolecoCore::ResetSound()
319
{
320
m_pAudio->Reset(m_pCartridge->IsPAL());
321
}
322
323
void GearcolecoCore::SaveRam()
324
{
325
SaveRam(NULL);
326
}
327
328
void GearcolecoCore::SaveRam(const char*, bool)
329
{
330
// TODO
331
}
332
333
void GearcolecoCore::LoadRam()
334
{
335
LoadRam(NULL);
336
}
337
338
void GearcolecoCore::LoadRam(const char*, bool)
339
{
340
// TODO
341
}
342
343
void GearcolecoCore::SaveState(int index)
344
{
345
Log("Creating save state %d...", index);
346
347
SaveState(NULL, index);
348
349
Debug("Save state %d created", index);
350
}
351
352
void GearcolecoCore::SaveState(const char* szPath, int index)
353
{
354
Log("Creating save state...");
355
356
using namespace std;
357
358
size_t size;
359
SaveState(NULL, size);
360
361
u8* buffer = new u8[size];
362
string path = "";
363
364
if (IsValidPointer(szPath))
365
{
366
path += szPath;
367
path += "/";
368
path += m_pCartridge->GetFileName();
369
}
370
else
371
{
372
path = m_pCartridge->GetFilePath();
373
}
374
375
string::size_type i = path.rfind('.', path.length());
376
377
if (i != string::npos) {
378
path.replace(i + 1, 3, "state");
379
}
380
381
std::stringstream sstm;
382
383
if (index < 0)
384
sstm << szPath;
385
else
386
sstm << path << index;
387
388
Log("Save state file: %s", sstm.str().c_str());
389
390
ofstream file;
391
open_ofstream_utf8(file, sstm.str().c_str(), ios::out | ios::binary);
392
393
SaveState(file, size);
394
395
SafeDeleteArray(buffer);
396
397
file.close();
398
399
Debug("Save state created");
400
}
401
402
bool GearcolecoCore::SaveState(u8* buffer, size_t& size)
403
{
404
bool ret = false;
405
406
if (m_pCartridge->IsReady())
407
{
408
using namespace std;
409
410
stringstream stream;
411
412
if (SaveState(stream, size))
413
ret = true;
414
415
if (IsValidPointer(buffer))
416
{
417
Log("Saving state to buffer [%d bytes]...", size);
418
memcpy(buffer, stream.str().c_str(), size);
419
ret = true;
420
}
421
}
422
else
423
{
424
Log("Invalid rom.");
425
}
426
427
return ret;
428
}
429
430
bool GearcolecoCore::SaveState(std::ostream& stream, size_t& size)
431
{
432
if (m_pCartridge->IsReady())
433
{
434
Debug("Gathering save state data...");
435
436
m_pMemory->SaveState(stream);
437
m_pProcessor->SaveState(stream);
438
m_pAudio->SaveState(stream);
439
m_pVideo->SaveState(stream);
440
m_pInput->SaveState(stream);
441
442
size = static_cast<size_t>(stream.tellp());
443
size += (sizeof(u32) * 2);
444
445
u32 header_magic = GC_SAVESTATE_MAGIC;
446
u32 header_size = static_cast<u32>(size);
447
448
stream.write(reinterpret_cast<const char*> (&header_magic), sizeof(header_magic));
449
stream.write(reinterpret_cast<const char*> (&header_size), sizeof(header_size));
450
451
Debug("Save state size: %d", static_cast<size_t>(stream.tellp()));
452
453
return true;
454
}
455
456
Log("Invalid rom.");
457
458
return false;
459
}
460
461
void GearcolecoCore::LoadState(int index)
462
{
463
Log("Loading save state %d...", index);
464
465
LoadState(NULL, index);
466
467
Debug("State %d file loaded", index);
468
}
469
470
void GearcolecoCore::LoadState(const char* szPath, int index)
471
{
472
Log("Loading save state...");
473
474
using namespace std;
475
476
string sav_path = "";
477
478
if (IsValidPointer(szPath))
479
{
480
sav_path += szPath;
481
sav_path += "/";
482
sav_path += m_pCartridge->GetFileName();
483
}
484
else
485
{
486
sav_path = m_pCartridge->GetFilePath();
487
}
488
489
string rom_path = sav_path;
490
491
string::size_type i = sav_path.rfind('.', sav_path.length());
492
493
if (i != string::npos) {
494
sav_path.replace(i + 1, 3, "state");
495
}
496
497
std::stringstream sstm;
498
499
if (index < 0)
500
sstm << szPath;
501
else
502
sstm << sav_path << index;
503
504
Log("Opening save file: %s", sstm.str().c_str());
505
506
ifstream file;
507
508
open_ifstream_utf8(file, sstm.str().c_str(), ios::in | ios::binary);
509
510
if (!file.fail())
511
{
512
if (LoadState(file))
513
{
514
Debug("Save state loaded");
515
}
516
}
517
else
518
{
519
Log("Save state file doesn't exist");
520
}
521
522
file.close();
523
}
524
525
bool GearcolecoCore::LoadState(const u8* buffer, size_t size)
526
{
527
if (m_pCartridge->IsReady() && (size > 0) && IsValidPointer(buffer))
528
{
529
Debug("Gathering load state data [%d bytes]...", size);
530
531
using namespace std;
532
533
stringstream stream;
534
535
stream.write(reinterpret_cast<const char*> (buffer), size);
536
537
return LoadState(stream);
538
}
539
540
Log("Invalid rom or memory.");
541
542
return false;
543
}
544
545
bool GearcolecoCore::LoadState(std::istream& stream)
546
{
547
if (m_pCartridge->IsReady())
548
{
549
using namespace std;
550
551
u32 header_magic = 0;
552
u32 header_size = 0;
553
554
stream.seekg(0, ios::end);
555
size_t size = static_cast<size_t>(stream.tellg());
556
stream.seekg(0, ios::beg);
557
558
Debug("Load state stream size: %d", size);
559
560
stream.seekg(size - (2 * sizeof(u32)), ios::beg);
561
stream.read(reinterpret_cast<char*> (&header_magic), sizeof(header_magic));
562
stream.read(reinterpret_cast<char*> (&header_size), sizeof(header_size));
563
stream.seekg(0, ios::beg);
564
565
Debug("Load state magic: 0x%08x", header_magic);
566
Debug("Load state size: %d", header_size);
567
568
if ((header_size == size) && (header_magic == GC_SAVESTATE_MAGIC))
569
{
570
Log("Loading state...");
571
572
m_pMemory->LoadState(stream);
573
m_pProcessor->LoadState(stream);
574
m_pAudio->LoadState(stream);
575
m_pVideo->LoadState(stream);
576
m_pInput->LoadState(stream);
577
578
return true;
579
}
580
else
581
{
582
Log("Invalid save state size or header");
583
}
584
}
585
else
586
{
587
Log("Invalid rom");
588
}
589
590
return false;
591
}
592
593
void GearcolecoCore::Reset()
594
{
595
m_pMemory->Reset();
596
m_pProcessor->Reset();
597
m_pAudio->Reset(m_pCartridge->IsPAL());
598
m_pVideo->Reset(m_pCartridge->IsPAL());
599
m_pInput->Reset();
600
m_pColecoVisionIOPorts->Reset();
601
m_bPaused = false;
602
}
603
604
void GearcolecoCore::RenderFrameBuffer(u8* finalFrameBuffer)
605
{
606
int size = m_pMemory->IsBiosLoaded() ? GC_RESOLUTION_WIDTH_WITH_OVERSCAN * GC_RESOLUTION_HEIGHT_WITH_OVERSCAN : GC_RESOLUTION_WIDTH * GC_RESOLUTION_HEIGHT;
607
u16* srcBuffer = (m_pMemory->IsBiosLoaded() ? m_pVideo->GetFrameBuffer() : kNoBiosImage);
608
609
switch (m_pixelFormat)
610
{
611
case GC_PIXEL_RGB555:
612
case GC_PIXEL_BGR555:
613
case GC_PIXEL_RGB565:
614
case GC_PIXEL_BGR565:
615
{
616
m_pVideo->Render16bit(srcBuffer, finalFrameBuffer, m_pixelFormat, size, true);
617
break;
618
}
619
case GC_PIXEL_RGB888:
620
case GC_PIXEL_BGR888:
621
{
622
m_pVideo->Render24bit(srcBuffer, finalFrameBuffer, m_pixelFormat, size, true);
623
break;
624
}
625
}
626
}
627
628