NINTV-DS/arm9/source/emucore/Intellivision.cpp
2021-09-02 17:32:31 -04:00

337 lines
9.6 KiB
C++

#include "Intellivision.h"
TYPEDEF_STRUCT_PACK( _IntellivisionState
{
StateHeader header;
StateChunk cpu;
CP1610State cpuState;
StateChunk stic;
AY38900State sticState;
StateChunk psg;
AY38914State psgState;
StateChunk RAM8bit;
RAMState RAM8bitState;
UINT16 RAM8bitImage[RAM8BIT_SIZE];
StateChunk RAM16bit;
RAMState RAM16bitState;
UINT16 RAM16bitImage[RAM16BIT_SIZE];
StateChunk GRAM;
RAMState GRAMState;
UINT16 GRAMImage[GRAM_SIZE];
StateChunk ivoice;
IntellivoiceState ivoiceState;
StateChunk ecs;
ECSState ecsState;
StateChunk eof;
} IntellivisionState; )
/**
* Initializes all of the basic hardware included in the Intellivision
* Master Component as well as the ECS and Intellivoice peripherals.
* This method is called only once when the Intellivision is constructed.
*/
Intellivision::Intellivision()
: Emulator("Intellivision"),
player1Controller(0, "Hand Controller #1"),
player2Controller(1, "Hand Controller #2"),
psg(0x01F0, &player1Controller, &player2Controller),
RAM8bit(RAM8BIT_SIZE, 0x0100, 8),
RAM16bit(RAM16BIT_SIZE, 0x0200, 16),
execROM("Executive ROM", "exec.bin", 0, 2, 0x1000, 0x1000),
grom("GROM", "grom.bin", 0, 1, 0x0800, 0x3000),
gram(),
cpu(&memoryBus, 0x1000, 0x1004),
stic(&memoryBus, &grom, &gram)
{
// define the video pixel dimensions
videoWidth = 160;
videoHeight = 192;
//make the pin connections from the CPU to the STIC
stic.connectPinOut(AY38900_PIN_OUT_SR1, &cpu, CP1610_PIN_IN_INTRM);
stic.connectPinOut(AY38900_PIN_OUT_SR2, &cpu, CP1610_PIN_IN_BUSRQ);
cpu.connectPinOut(CP1610_PIN_OUT_BUSAK, &stic, AY38900_PIN_IN_SST);
//add the player one hand controller
AddInputConsumer(&player1Controller);
//add the player two hand controller
AddInputConsumer(&player2Controller);
//add the 8-bit RAM
AddRAM(&RAM8bit);
//add the 16-bit RAM
AddRAM(&RAM16bit);
//add the executive ROM
AddROM(&execROM);
//add the GROM
AddROM(&grom);
//add the GRAM
AddRAM(&gram);
//add the backtab ram
AddRAM(&stic.backtab);
//add the CPU
AddProcessor(&cpu);
//add the STIC
AddProcessor(&stic);
AddVideoProducer(&stic);
//add the STIC registers
AddRAM(&stic.registers);
//add the PSG
AddProcessor(&psg);
AddAudioProducer(&psg);
//add the PSG registers
AddRAM(&psg.registers);
AddPeripheral(&ecs);
AddPeripheral(&intellivoice);
}
BOOL Intellivision::SaveState(const CHAR* filename)
{
BOOL didSave = FALSE;
IntellivisionState state = {9};
size_t totalStateSize = sizeof(IntellivisionState);
size_t hsize = sizeof(StateHeader);
size_t csize = sizeof(StateChunk);
size_t cpusize = sizeof(CP1610State);
size_t sticsize = sizeof(AY38900State);
size_t psgsize = sizeof(AY38914State);
size_t ivoicesize = sizeof(IntellivoiceState);
size_t ecssize = sizeof(ECSState);
size_t ramsize = sizeof(RAMState);
size_t ram8imgsize = sizeof(state.RAM8bitImage);
size_t ram16imgsize = sizeof(state.RAM16bitImage);
size_t gramimgsize = sizeof(state.GRAMImage);
state.header.emu = FOURCHAR('EMUS');
state.header.state = FOURCHAR('TATE');
state.header.emuID = ID_EMULATOR_BLISS;
state.header.version = FOURCHAR(EMU_STATE_VERSION);
state.header.sys = FOURCHAR('SYS\0');
state.header.sysID = ID_SYSTEM_INTELLIVISION;
state.header.cart = FOURCHAR('CART');
state.header.cartID = currentRip->GetCRC();
state.cpu.id = FOURCHAR('CPU\0');
state.cpu.size = sizeof(CP1610State);
state.cpuState = cpu.getState();
state.stic.id = FOURCHAR('STIC');
state.stic.size = sizeof(AY38900State);
state.sticState = stic.getState();
state.psg.id = FOURCHAR('PSG\0');
state.psg.size = sizeof(AY38914State);
state.psgState = psg.getState();
state.RAM8bit.id = FOURCHAR('RAM0');
state.RAM8bit.size = sizeof(RAMState) + sizeof(state.RAM8bitImage);
state.RAM8bitState = RAM8bit.getState(state.RAM8bitImage);
state.RAM16bit.id = FOURCHAR('RAM1');
state.RAM16bit.size = sizeof(RAMState) + sizeof(state.RAM16bitImage);
state.RAM16bitState = RAM16bit.getState(state.RAM16bitImage);
state.GRAM.id = FOURCHAR('GRAM');
state.GRAM.size = sizeof(RAMState) + sizeof(state.GRAMImage);
state.GRAMState = gram.getState(state.GRAMImage);
// TODO: only if ivoice is used for this cart?
state.ivoice.id = FOURCHAR('VOIC');
state.ivoice.size = sizeof(IntellivoiceState);
state.ivoiceState = intellivoice.getState();
// TODO: only if ecs is used for this cart?
state.ecs.id = FOURCHAR('ECS\0');
state.ecs.size = sizeof(ECSState);
state.ecsState = ecs.getState();
state.eof.id = FOURCHAR('EOF\0');
state.eof.size = sizeof(IntellivisionState);
FILE* file = fopen(filename, "wb");
if (file == NULL) {
printf("Error: Unable to create file %s\n", filename);
didSave = FALSE;
}
if (file != NULL && totalStateSize == fwrite(&state, 1, totalStateSize, file)) {
didSave = TRUE;
} else {
printf("Error: could not write %zu bytes to file %s\n", totalStateSize, filename);
didSave = FALSE;
}
if (file) {
fclose(file);
file = NULL;
}
return didSave;
}
BOOL Intellivision::LoadState(const CHAR* filename)
{
BOOL didLoadState = FALSE;
IntellivisionState state = {9};
size_t totalStateSize = sizeof(IntellivisionState);
FILE* file = fopen(filename, "rb");
if (file == NULL) {
printf("Error: Unable to open file %s\n", filename);
return FALSE;
}
#if 0
// read in the whole file
if (totalStateSize != fread(&state, 1, totalStateSize, file)) {
printf("Error: could not read state (%zu bytes) from file %s\n", totalStateSize, filename);
goto close;
}
#else
BOOL isParsing = FALSE;
StateChunk chunk = {0};
// read in the header
if (sizeof(StateHeader) != fread(&state, 1, sizeof(StateHeader), file)) {
printf("Error: could not read state header (%zu bytes) from file %s\n", totalStateSize, filename);
goto close;
}
// validate file header
if (state.header.emu != FOURCHAR('EMUS') || state.header.state != FOURCHAR('TATE')) {
printf("Error: invalid header in file %s\n", filename);
goto close;
}
if (state.header.emuID != ID_EMULATOR_BLISS) {
printf("Error: invalid emulator ID %x in file %s\n", state.header.emuID, filename);
goto close;
}
if (FOURCHAR(EMU_STATE_VERSION) != FOURCHAR('dev\0') && state.header.version != FOURCHAR('dev\0') && state.header.version != FOURCHAR(EMU_STATE_VERSION)) {
printf("Error: invalid emulator version 0x%08x (expected 0x%08x) in file %s\n", state.header.version, EMU_STATE_VERSION, filename);
goto close;
}
if (state.header.sys != FOURCHAR('SYS\0')) {
printf("Error: expected 'SYS ' chunk in file %s\n", filename);
goto close;
}
if (state.header.sysID != ID_SYSTEM_INTELLIVISION) {
printf("Error: invalid system ID %x in file %s\n", state.header.sysID, filename);
goto close;
}
if (state.header.cart != FOURCHAR('CART')) {
printf("Error: expected 'CART' chunk in file %s\n", filename);
goto close;
}
if (state.header.cartID != 0x00000000 && state.header.cartID != currentRip->GetCRC()) {
printf("Error: cartridge mismatch in file %s\n", filename);
goto close;
}
isParsing = TRUE;
while (isParsing) {
size_t fpos = ftell(file);
if (sizeof(StateChunk) != fread(&chunk, 1, sizeof(StateChunk), file)) {
isParsing = FALSE;
break;
}
switch (chunk.id) {
default:
fpos = ftell(file);
break;
case FOURCHAR('CPU\0'):
if (chunk.size == sizeof(state.cpuState)) {
state.cpu = chunk;
fread(&state.cpuState, 1, state.cpu.size, file);
}
break;
case FOURCHAR('STIC'):
if (chunk.size == sizeof(state.sticState)) {
state.stic = chunk;
fread(&state.sticState, 1, state.stic.size, file);
}
break;
case FOURCHAR('PSG\0'):
if (chunk.size == sizeof(state.psgState)) {
state.psg = chunk;
fread(&state.psgState, 1, state.psg.size, file);
}
break;
case FOURCHAR('RAM0'):
if (chunk.size == sizeof(state.RAM8bitState) + sizeof(state.RAM8bitImage)) {
state.RAM8bit = chunk;
fread(&state.RAM8bitState, 1, state.RAM8bit.size, file);
}
break;
case FOURCHAR('RAM1'):
if (chunk.size == sizeof(state.RAM16bitState) + sizeof(state.RAM16bitImage)) {
state.RAM16bit = chunk;
fread(&state.RAM16bitState, 1, state.RAM16bit.size, file);
}
break;
case FOURCHAR('GRAM'):
if (chunk.size == sizeof(state.GRAMState) + sizeof(state.GRAMImage)) {
state.GRAM = chunk;
fread(&state.GRAMState, 1, state.GRAM.size, file);
}
break;
case FOURCHAR('VOIC'):
// TODO: only if ivoice/ecs is used for this cart?
if (chunk.size == sizeof(state.ivoiceState)) {
state.ivoice = chunk;
fread(&state.ivoiceState, 1, state.ivoice.size, file);
}
break;
case FOURCHAR('ECS\0'):
// TODO: only if ivoice/ecs is used for this cart?
if (chunk.size == sizeof(state.ecsState)) {
state.ecs = chunk;
fread(&state.ecsState, 1, state.ecs.size, file);
}
break;
case FOURCHAR('EOF\0'):
state.eof = chunk;
isParsing = FALSE;
break;
}
}
#endif
didLoadState = TRUE;
cpu.setState(state.cpuState);
stic.setState(state.sticState);
psg.setState(state.psgState);
RAM8bit.setState(state.RAM8bitState, state.RAM8bitImage);
RAM16bit.setState(state.RAM16bitState, state.RAM16bitImage);
gram.setState(state.GRAMState, state.GRAMImage);
intellivoice.setState(state.ivoiceState);
ecs.setState(state.ecsState);
close:
fclose(file);
file = NULL;
end:
return didLoadState;
}