From 3e6151cf0f082c3b5ed4c75c705dab45e43a1622 Mon Sep 17 00:00:00 2001 From: jeremiah sypult Date: Mon, 27 Oct 2014 23:03:26 -0500 Subject: [PATCH] Intellivision load/save states --- ReleaseNotes.txt | 6 + ToDo.txt | 3 +- core/Emulator.cpp | 53 +++--- core/Emulator.h | 36 ++++- core/audio/AY38914.cpp | 67 ++++++++ core/audio/AY38914.h | 31 +++- core/audio/AY38914_Channel.cpp | 30 ++++ core/audio/AY38914_Channel.h | 17 +- core/audio/AY38914_Registers.h | 13 +- core/audio/AudioMixer.cpp | 5 +- core/audio/AudioMixer.h | 8 +- core/audio/SP0256.cpp | 67 ++++++++ core/audio/SP0256.h | 33 +++- core/cpu/CP1610.cpp | 33 ++++ core/cpu/CP1610.h | 19 ++- core/memory/RAM.cpp | 33 ++++ core/memory/RAM.h | 26 ++- core/rip/Rip.cpp | 26 ++- core/rip/Rip.h | 14 ++ core/rip/knowncarts.cfg | 101 +++++++++++- core/types.h | 28 +++- core/video/AY38900.cpp | 55 ++++++- core/video/AY38900.h | 39 +++-- core/video/AY38900_Registers.h | 11 +- core/video/BackTabRAM.cpp | 19 +++ core/video/BackTabRAM.h | 19 +++ core/video/GRAM.cpp | 24 +++ core/video/GRAM.h | 16 +- core/video/MOB.cpp | 46 ++++++ core/video/MOB.h | 23 ++- drivers/a5200/Atari5200.cpp | 11 ++ drivers/a5200/Atari5200.h | 4 +- drivers/intv/ECS.cpp | 20 ++- drivers/intv/ECS.h | 13 ++ drivers/intv/Intellivision.cpp | 276 +++++++++++++++++++++++++++++++- drivers/intv/Intellivision.h | 8 +- drivers/intv/Intellivoice.cpp | 13 ++ drivers/intv/Intellivoice.h | 10 +- sys/windows/Bliss.rc | 12 +- sys/windows/BlissApp.cpp | 0 sys/windows/BlissAudioVideo.cpp | 0 sys/windows/BlissAudioVideo.h | 0 sys/windows/BlissMainFrame.cpp | 69 ++++++++ sys/windows/BlissMainFrame.h | 2 + sys/windows/resource.h | 2 + 45 files changed, 1254 insertions(+), 87 deletions(-) mode change 100755 => 100644 sys/windows/BlissApp.cpp mode change 100755 => 100644 sys/windows/BlissAudioVideo.cpp mode change 100755 => 100644 sys/windows/BlissAudioVideo.h mode change 100755 => 100644 sys/windows/BlissMainFrame.cpp mode change 100755 => 100644 sys/windows/BlissMainFrame.h diff --git a/ReleaseNotes.txt b/ReleaseNotes.txt index 7e7b728..6247a3b 100644 --- a/ReleaseNotes.txt +++ b/ReleaseNotes.txt @@ -1,3 +1,9 @@ +v2.1.0 - October 27, 2014 +- Added support for saving and loading Intellivision game states. +- Refactored code for multi-platform support. + See https://github.com/jeremiah-sypult/BlissEmu and + https://github.com/OpenEmu/OpenEmu + v2.0.5 - May 25, 2005 - Added support for the Classic Game Controller (CGC). See http://www.shiny-technologies.com/cgc.php for details on this device. diff --git a/ToDo.txt b/ToDo.txt index 9d5d406..7528d5f 100644 --- a/ToDo.txt +++ b/ToDo.txt @@ -3,11 +3,12 @@ - offer the ability to display the overlays somehow, maybe as a toggle display for the lower left and right sides of the screen - input + - move Classic Game Controller (CGC) support code out of core (Windows) - bug where I can't add bindings to the ECS keyboard default bindings; have to clear the binding first because it's getting confused somehow - allow the user to custom-bind different controls to different games - saving and loading - - saving and loading game state + - saving and loading atari 5200 game state - saving screenshots - record/playback (save the inputs and play them back) - record audio diff --git a/core/Emulator.cpp b/core/Emulator.cpp index fdad1b1..d55b2e8 100755 --- a/core/Emulator.cpp +++ b/core/Emulator.cpp @@ -58,52 +58,56 @@ void Emulator::UsePeripheral(UINT32 i, BOOL b) UINT32 Emulator::GetVideoWidth() { - return videoWidth; + return videoWidth; } UINT32 Emulator::GetVideoHeight() { - return videoHeight; + return videoHeight; } void Emulator::InitVideo(VideoBus* video, UINT32 width, UINT32 height) { - if ( video != NULL ) { - videoBus = video; - } + if ( video != NULL ) { + videoBus = video; + } videoBus->init(width, height); } void Emulator::ReleaseVideo() { - videoBus->release(); - videoBus = NULL; + if (videoBus) { + videoBus->release(); + videoBus = NULL; + } } void Emulator::InitAudio(AudioMixer* audio, UINT32 sampleRate) { - if (audio != NULL) { - audioMixer = audio; - } + if (audio != NULL) { + audioMixer = audio; + } - // TODO: check for an existing audioMixer processor and release it - for (UINT16 i = 0; i < GetProcessorCount(); i++) { - Processor* p = GetProcessor(i); - if (p == audio) { - RemoveProcessor(audio); - } - } + // TODO: check for an existing audioMixer processor and release it + for (UINT16 i = 0; i < GetProcessorCount(); i++) { + Processor* p = GetProcessor(i); + if (p == audio) { + RemoveProcessor(audio); + } + } - AddProcessor(audioMixer); - audioMixer->init(sampleRate); + AddProcessor(audioMixer); + audioMixer->init(sampleRate); } void Emulator::ReleaseAudio() { - audioMixer->release(); - RemoveProcessor(audioMixer); - audioMixer = NULL; + if (audioMixer) { + audioMixer->release(); + RemoveProcessor(audioMixer); + audioMixer = NULL; + } } void Emulator::Reset() @@ -141,7 +145,7 @@ void Emulator::SetRip(Rip* rip) void Emulator::InsertPeripheral(Peripheral* p) { - UINT16 i; + UINT16 i; //processors UINT16 count = p->GetProcessorCount(); @@ -179,7 +183,7 @@ void Emulator::InsertPeripheral(Peripheral* p) void Emulator::RemovePeripheral(Peripheral* p) { - UINT16 i; + UINT16 i; //processors UINT16 count = p->GetProcessorCount(); @@ -240,4 +244,3 @@ Emulator* Emulator::emus[] = { &atari5200, &inty, }; - diff --git a/core/Emulator.h b/core/Emulator.h index d749928..ef138f9 100755 --- a/core/Emulator.h +++ b/core/Emulator.h @@ -16,6 +16,30 @@ #include "core/memory/MemoryBus.h" #include "core/memory/Memory.h" +typedef struct _StateHeader +{ + UINT32 emu; + UINT32 state; + UINT32 emuID; + UINT32 version; + UINT32 sys; + UINT32 sysID; + UINT32 cart; + UINT32 cartID; +} StateHeader; + +typedef struct _StateChunk +{ + UINT32 id; + UINT32 size; +} StateChunk; + +#if defined(DEBUG) +#define EMU_STATE_VERSION ('dev\0') +#else +#define EMU_STATE_VERSION (0x02010000) +#endif + class Intellivision; class Atari5200; @@ -49,6 +73,9 @@ class Emulator : public Peripheral void FlushAudio(); void Render(); + virtual BOOL SaveState(const CHAR* filename) = 0; + virtual BOOL LoadState(const CHAR* filename) = 0; + static UINT32 GetEmulatorCount(); static Emulator* GetEmulator(UINT32 i); static Emulator* GetEmulatorByID(UINT32 targetSystemID); @@ -58,8 +85,10 @@ class Emulator : public Peripheral MemoryBus memoryBus; - UINT32 videoWidth; - UINT32 videoHeight; + Rip* currentRip; + + UINT32 videoWidth; + UINT32 videoHeight; private: ProcessorBus processorBus; @@ -70,8 +99,6 @@ class Emulator : public Peripheral void InsertPeripheral(Peripheral* p); void RemovePeripheral(Peripheral* p); - Rip* currentRip; - Peripheral* peripherals[MAX_PERIPHERALS]; BOOL usePeripheralIndicators[MAX_PERIPHERALS]; INT32 peripheralCount; @@ -84,4 +111,3 @@ class Emulator : public Peripheral }; #endif - diff --git a/core/audio/AY38914.cpp b/core/audio/AY38914.cpp index 2737c5e..d7a7966 100644 --- a/core/audio/AY38914.cpp +++ b/core/audio/AY38914.cpp @@ -221,3 +221,70 @@ INT32 AY38914::tick(INT32 minimum) return totalTicks; } +AY38914State AY38914::getState() +{ + AY38914State state = {0}; + + this->registers.getMemory(state.registers, 0, this->registers.getMemoryByteSize()); + + state.clockDivisor = this->clockDivisor; + + state.channel0 = this->channel0.getState(); + state.channel1 = this->channel1.getState(); + state.channel2 = this->channel2.getState(); + + state.cachedTotalOutputIsDirty = this->cachedTotalOutputIsDirty; + state.cachedTotalOutput = this->cachedTotalOutput; + + state.envelopeIdle = this->envelopeIdle; + state.envelopePeriod = this->envelopePeriod; + state.envelopePeriodValue = this->envelopePeriodValue; + state.envelopeCounter = this->envelopeCounter; + state.envelopeVolume = this->envelopeVolume; + state.envelopeHold = this->envelopeHold; + state.envelopeAltr = this->envelopeAltr; + state.envelopeAtak = this->envelopeAtak; + state.envelopeCont = this->envelopeCont; + + state.noiseIdle = this->noiseIdle; + state.noisePeriod = this->noisePeriod; + state.noisePeriodValue = this->noisePeriodValue; + state.noiseCounter = this->noiseCounter; + + state.random = this->random; + state.noise = this->noise; + + return state; +} + +void AY38914::setState(AY38914State state) +{ + this->registers.setMemory(state.registers, 0, this->registers.getMemoryByteSize()); + + this->clockDivisor = state.clockDivisor; + + this->channel0.setState(state.channel0); + this->channel1.setState(state.channel1); + this->channel2.setState(state.channel2); + + this->cachedTotalOutputIsDirty = state.cachedTotalOutputIsDirty; + this->cachedTotalOutput = state.cachedTotalOutput; + + this->envelopeIdle = state.envelopeIdle; + this->envelopePeriod = state.envelopePeriod; + this->envelopePeriodValue = state.envelopePeriodValue; + this->envelopeCounter = state.envelopeCounter; + this->envelopeVolume = state.envelopeVolume; + this->envelopeHold = state.envelopeHold; + this->envelopeAltr = state.envelopeAltr; + this->envelopeAtak = state.envelopeAtak; + this->envelopeCont = state.envelopeCont; + + this->noiseIdle = state.noiseIdle; + this->noisePeriod = state.noisePeriod; + this->noisePeriodValue = state.noisePeriodValue; + this->noiseCounter = state.noiseCounter; + + this->random = state.random; + this->noise = state.noise; +} diff --git a/core/audio/AY38914.h b/core/audio/AY38914.h index 74c7347..bc26bba 100644 --- a/core/audio/AY38914.h +++ b/core/audio/AY38914.h @@ -12,6 +12,32 @@ class Intellivision; +TYPEDEF_STRUCT_PACK( _AY38914State +{ + UINT16 registers[0x0E]; + AY38914_ChannelState channel0; + AY38914_ChannelState channel1; + AY38914_ChannelState channel2; + INT32 clockDivisor; + INT32 cachedTotalOutput; + INT32 envelopePeriod; + INT32 envelopePeriodValue; + INT32 envelopeCounter; + INT32 envelopeVolume; + INT32 noisePeriod; + INT32 noisePeriodValue; + INT32 noiseCounter; + INT32 random; + INT8 cachedTotalOutputIsDirty; + INT8 envelopeIdle; + INT8 envelopeHold; + INT8 envelopeAltr; + INT8 envelopeAtak; + INT8 envelopeCont; + INT8 noiseIdle; + INT8 noise; +} AY38914State; ) + /** * The AY-3-8914 chip in the Intellivision, also known as the Programmable * Sound Generator (PSG). @@ -35,6 +61,9 @@ class AY38914 : public Processor, public AudioProducer void setClockDivisor(INT32 clockDivisor); INT32 getClockDivisor(); + AY38914State getState(); + void setState(AY38914State state); + //registers AY38914_Registers registers; @@ -77,8 +106,6 @@ class AY38914 : public Processor, public AudioProducer //output amplitudes for a single channel static const INT32 amplitudes16Bit[16]; - }; #endif - diff --git a/core/audio/AY38914_Channel.cpp b/core/audio/AY38914_Channel.cpp index 44e6dae..7bc725d 100644 --- a/core/audio/AY38914_Channel.cpp +++ b/core/audio/AY38914_Channel.cpp @@ -13,3 +13,33 @@ void AY38914_Channel::reset() { isDirty = TRUE; cachedSample = 0; } + +AY38914_ChannelState AY38914_Channel::getState() +{ + AY38914_ChannelState state = {0}; + + state.period = this->period; + state.periodValue = this->periodValue; + state.volume = this->volume; + state.toneCounter = this->toneCounter; + state.tone = this->tone; + state.envelope = this->envelope; + state.toneDisabled = this->toneDisabled; + state.noiseDisabled = this->noiseDisabled; + + return state; +} + +void AY38914_Channel::setState(AY38914_ChannelState state) +{ + this->period = state.period; + this->periodValue = state.periodValue; + this->volume = state.volume; + this->toneCounter = state.toneCounter; + this->tone = state.tone; + this->envelope = state.envelope; + this->toneDisabled = state.toneDisabled; + this->noiseDisabled = state.noiseDisabled; + + this->isDirty = TRUE; +} diff --git a/core/audio/AY38914_Channel.h b/core/audio/AY38914_Channel.h index e2cdc1a..468bd0a 100644 --- a/core/audio/AY38914_Channel.h +++ b/core/audio/AY38914_Channel.h @@ -7,12 +7,27 @@ class AY38914; class AY38914_Registers; +TYPEDEF_STRUCT_PACK( _AY38914_ChannelState +{ + INT32 period; + INT32 periodValue; + INT32 volume; + INT32 toneCounter; + INT8 tone; + INT8 envelope; + INT8 toneDisabled; + INT8 noiseDisabled; +} AY38914_ChannelState; ) + class AY38914_Channel { friend class AY38914; friend class AY38914_Registers; + AY38914_ChannelState getState(); + void setState(AY38914_ChannelState state); + private: void reset(); @@ -26,8 +41,6 @@ class AY38914_Channel BOOL noiseDisabled; BOOL isDirty; INT32 cachedSample; - }; #endif - diff --git a/core/audio/AY38914_Registers.h b/core/audio/AY38914_Registers.h index 142a3e9..d782272 100644 --- a/core/audio/AY38914_Registers.h +++ b/core/audio/AY38914_Registers.h @@ -8,7 +8,6 @@ class AY38914; class AY38914_Registers : public RAM { - friend class AY38914; public: @@ -16,14 +15,22 @@ class AY38914_Registers : public RAM void poke(UINT16 location, UINT16 value); UINT16 peek(UINT16 location); + inline size_t getMemoryByteSize() { + return sizeof(memory); + } + void getMemory(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, memory + offset, size); + } + void setMemory(void* src, UINT16 offset, UINT16 size) { + memcpy(memory + offset, src, size); + } + private: AY38914_Registers(UINT16 address); void init(AY38914* ay38914); AY38914* ay38914; UINT16 memory[0x0E]; - }; #endif - diff --git a/core/audio/AudioMixer.cpp b/core/audio/AudioMixer.cpp index a8dc9ae..bf964ff 100755 --- a/core/audio/AudioMixer.cpp +++ b/core/audio/AudioMixer.cpp @@ -13,7 +13,8 @@ AudioMixer::AudioMixer() sampleBuffer(NULL), sampleBufferSize(0), sampleCount(0), - sampleSize(0) + sampleSize(0), + gain(1.0f) { memset(&audioProducers, 0, sizeof(audioProducers)); } @@ -145,7 +146,7 @@ INT32 AudioMixer::tick(INT32 minimum) totalSample = totalSample / audioProducerCount; } - sampleBuffer[sampleCount++] = clipSample(totalSample); + sampleBuffer[sampleCount++] = clipSample((totalSample * this->gain) + 0.5f); if (sampleCount == sampleSize) { flushAudio(); diff --git a/core/audio/AudioMixer.h b/core/audio/AudioMixer.h index 981d1d0..871dc00 100755 --- a/core/audio/AudioMixer.h +++ b/core/audio/AudioMixer.h @@ -21,7 +21,7 @@ class AudioMixer : public Processor virtual ~AudioMixer(); inline INT16 clipSample(INT64 sample) { - return sample > 32767 ? 32767 : sample < -32768 ? -32768 : sample; + return sample > 32767 ? 32767 : sample < -32768 ? -32768 : (INT16)sample; } virtual void resetProcessor(); @@ -37,6 +37,10 @@ class AudioMixer : public Processor void removeAudioProducer(AudioProducer*); void removeAll(); + void setGain(float g) { + this->gain = g; + } + protected: //output info INT32 clockSpeed; @@ -49,6 +53,8 @@ class AudioMixer : public Processor UINT32 sampleBufferSize; UINT32 sampleCount; UINT32 sampleSize; + + float gain; }; #endif diff --git a/core/audio/SP0256.cpp b/core/audio/SP0256.cpp index b8e8045..51e83d6 100644 --- a/core/audio/SP0256.cpp +++ b/core/audio/SP0256.cpp @@ -853,3 +853,70 @@ INT32 SP0256::flipEndian(INT32 value, INT32 bits) { return output; } +SP0256State SP0256::getState() +{ + SP0256State state = {0}; + + state.bitsLeft = this->bitsLeft; + state.currentBits = this->currentBits; + + state.pc = this->pc; + state.stack = this->stack; + state.mode = this->mode; + state.repeatPrefix = this->repeatPrefix; + state.page = this->page; + state.command = this->command; + + state.idle = this->idle; + state.lrqHigh = this->lrqHigh; + state.speaking = this->speaking; + memcpy(state.fifoBytes, this->fifoBytes, sizeof(this->fifoBytes)); + state.fifoHead = this->fifoHead; + state.fifoSize = this->fifoSize; + + state.repeat = this->repeat; + state.period = this->period; + state.periodCounter = this->periodCounter; + state.amplitude = this->amplitude; + memcpy(state.b, this->b, sizeof(this->b)); + memcpy(state.f, this->f, sizeof(this->f)); + memcpy(state.y, this->y, sizeof(this->f)); + state.periodInterpolation = this->periodInterpolation; + state.amplitudeInterpolation = this->amplitudeInterpolation; + + state.random = this->random; + + return state; +} + +void SP0256::setState(SP0256State state) +{ + this->bitsLeft = state.bitsLeft; + this->currentBits = state.currentBits; + + this->pc = state.pc; + this->stack = state.stack; + this->mode = state.mode; + this->repeatPrefix = state.repeatPrefix; + this->page = state.page; + this->command = state.command; + + this->idle = state.idle; + this->lrqHigh = state.lrqHigh; + this->speaking = state.speaking; + memcpy(this->fifoBytes, state.fifoBytes, sizeof(this->fifoBytes)); + this->fifoHead = state.fifoHead; + this->fifoSize = state.fifoSize; + + this->repeat = state.repeat; + this->period = state.period; + this->periodCounter = state.periodCounter; + this->amplitude = state.amplitude; + memcpy(this->b, state.b, sizeof(this->b)); + memcpy(this->f, state.f, sizeof(this->f)); + memcpy(this->y, state.y, sizeof(this->f)); + this->periodInterpolation = state.periodInterpolation; + this->amplitudeInterpolation = state.amplitudeInterpolation; + + this->random = state.random; +} diff --git a/core/audio/SP0256.h b/core/audio/SP0256.h index 8ff2080..639393b 100644 --- a/core/audio/SP0256.h +++ b/core/audio/SP0256.h @@ -9,6 +9,35 @@ #include "core/memory/ROM.h" #include "AudioOutputLine.h" +TYPEDEF_STRUCT_PACK( _SP0256State +{ + INT32 bitsLeft; + INT32 currentBits; + INT32 pc; + INT32 stack; + INT32 mode; + INT32 repeatPrefix; + INT32 page; + INT32 command; + INT32 repeat; + INT32 period; + INT32 periodCounter; + INT32 amplitude; + INT32 random; + INT32 fifoHead; + INT32 fifoSize; + INT32 fifoBytes[64]; + INT32 y[6][2]; + INT8 b[6]; + INT8 f[6]; + INT8 periodInterpolation; + INT8 amplitudeInterpolation; + INT8 idle; + INT8 lrqHigh; + INT8 speaking; + UINT8 _pad6[3]; +} SP0256State; ) + class SP0256 : public Processor, public AudioProducer { @@ -23,6 +52,9 @@ class SP0256 : public Processor, public AudioProducer INT32 tick(INT32); inline BOOL isIdle() { return idle; } + SP0256State getState(); + void setState(SP0256State state); + SP0256_Registers registers; ROM ivoiceROM; @@ -89,7 +121,6 @@ class SP0256 : public Processor, public AudioProducer //coefficient table static const INT32 qtbl[256]; - }; #endif diff --git a/core/cpu/CP1610.cpp b/core/cpu/CP1610.cpp index 9ce76ee..819b2c4 100644 --- a/core/cpu/CP1610.cpp +++ b/core/cpu/CP1610.cpp @@ -4642,3 +4642,36 @@ INT32 CP1610::decode(UINT16 op) } } +CP1610State CP1610::getState() +{ + CP1610State state = {0}; + + state.interruptAddress = this->interruptAddress; + state.resetAddress = this->resetAddress; + memcpy(state.r, this->r, sizeof(this->r)); + state.S = this->S; + state.Z = this->Z; + state.O = this->O; + state.C = this->C; + state.I = this->I; + state.D = this->D; + state.interruptible = this->interruptible; + state.ext = this->ext; + + return state; +} + +void CP1610::setState(CP1610State state) +{ + this->interruptAddress = state.interruptAddress; + this->resetAddress = state.resetAddress; + memcpy(this->r, state.r, sizeof(this->r)); + this->S = state.S; + this->Z = state.Z; + this->O = state.O; + this->C = state.C; + this->I = state.I; + this->D = state.D; + this->interruptible = state.interruptible; + this->ext = state.ext; +} diff --git a/core/cpu/CP1610.h b/core/cpu/CP1610.h index 2eba304..5b5cac8 100644 --- a/core/cpu/CP1610.h +++ b/core/cpu/CP1610.h @@ -11,6 +11,21 @@ #define CP1610_PIN_OUT_BUSAK 0 +TYPEDEF_STRUCT_PACK( _CP1610State +{ + INT8 S; + INT8 Z; + INT8 O; + INT8 C; + INT8 I; + INT8 D; + INT8 interruptible; + INT8 ext; + UINT16 interruptAddress; + UINT16 resetAddress; + UINT16 r[8]; +} CP1610State; ) + class CP1610 : public Processor { @@ -46,7 +61,8 @@ class CP1610 : public Processor UINT32 decode(CHAR description[256], UINT32 memoryLocation); UINT32 getProgramCounter(); #endif - + CP1610State getState(); + void setState(CP1610State state); private: void setIndirect(UINT16 register, UINT16 value); @@ -148,7 +164,6 @@ class CP1610 : public Processor //the four external lines INT8 ext; - }; #endif diff --git a/core/memory/RAM.cpp b/core/memory/RAM.cpp index bba5742..278695a 100644 --- a/core/memory/RAM.cpp +++ b/core/memory/RAM.cpp @@ -115,3 +115,36 @@ void RAM::poke(UINT16 location, UINT16 value) image[(location&writeAddressMask)-this->location] = (value & trimmer); } +RAMState RAM::getState(UINT16* image) +{ + RAMState state = {0}; + + state.enabled = this->enabled; + state.size = this->size; + state.location = this->location; + state.readAddressMask = this->readAddressMask; + state.writeAddressMask = this->writeAddressMask; + state.bitWidth = this->bitWidth; + state.trimmer = this->trimmer; + + if (image != NULL) { + this->getImage(image, 0, this->getImageByteSize()); + } + + return state; +} + +void RAM::setState(RAMState state, UINT16* image) +{ + this->enabled = state.enabled; + this->size = state.size; + this->location = state.location; + this->readAddressMask = state.readAddressMask; + this->writeAddressMask = state.writeAddressMask; + this->bitWidth = state.bitWidth; + this->trimmer = state.trimmer; + + if (image != NULL) { + this->setImage(image, 0, this->getImageByteSize()); + } +} diff --git a/core/memory/RAM.h b/core/memory/RAM.h index 5d47994..77bdc21 100644 --- a/core/memory/RAM.h +++ b/core/memory/RAM.h @@ -2,8 +2,20 @@ #ifndef RAM_H #define RAM_H +#include #include "Memory.h" +TYPEDEF_STRUCT_PACK( _RAMState +{ + INT8 enabled; + UINT8 bitWidth; + UINT16 size; + UINT16 location; + UINT16 readAddressMask; + UINT16 writeAddressMask; + UINT16 trimmer; +} RAMState; ) + class RAM : public Memory { @@ -27,6 +39,19 @@ class RAM : public Memory UINT16 getWriteAddressMask(); virtual void poke(UINT16 location, UINT16 value); + inline size_t getImageByteSize() { + return size * sizeof(UINT16); + } + void getImage(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, image + offset, size); + } + void setImage(void* src, UINT16 offset, UINT16 size) { + memcpy(image + offset, src, size); + } + + RAMState getState(UINT16* image); + void setState(RAMState state, UINT16* image); + //enabled attributes void SetEnabled(BOOL b); BOOL IsEnabled() { return enabled; } @@ -42,7 +67,6 @@ class RAM : public Memory UINT8 bitWidth; UINT16 trimmer; UINT16* image; - }; #endif diff --git a/core/rip/Rip.cpp b/core/rip/Rip.cpp index 1492c0e..6cac3db 100644 --- a/core/rip/Rip.cpp +++ b/core/rip/Rip.cpp @@ -20,12 +20,14 @@ Rip::Rip(UINT32 systemID) : Peripheral("", ""), peripheralCount(0), - targetSystemID(systemID) + targetSystemID(systemID), + crc(0) { producer = new CHAR[1]; strcpy(producer, ""); year = new CHAR[1]; strcpy(year, ""); + memset(filename, 0, sizeof(filename)); } Rip::~Rip() @@ -130,6 +132,9 @@ Rip* Rip::LoadA52(const CHAR* filename) delete[] image; + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + return rip; } @@ -178,6 +183,9 @@ Rip* Rip::LoadBin(const CHAR* filename, const CHAR* configFile) delete[] image; + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + return rip; } @@ -318,10 +326,10 @@ Rip* Rip::LoadCartridgeConfiguration(const CHAR* configFile, UINT32 crc) continue; PeripheralCompatibility pc; - if (strcmpi(nextToken+1, "optional") != 0) - pc = PERIPH_OPTIONAL; - else if (strcmpi(nextToken+1, "required") != 0) + if (strcmpi(nextToken+1, "required") != 0) pc = PERIPH_REQUIRED; + else if (strcmpi(nextToken+1, "optional") != 0) + pc = PERIPH_OPTIONAL; else continue; @@ -465,6 +473,9 @@ Rip* Rip::LoadRom(const CHAR* filename) } fclose(infile); + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + return rip; } @@ -663,6 +674,9 @@ Rip* Rip::LoadRip(const CHAR* filename) } fclose(file); + rip->SetFileName(filename); + rip->crc = CRC32::getCrc(filename); + return rip; } @@ -718,6 +732,9 @@ Rip* Rip::LoadZip(const CHAR* filename, const CHAR* configFile) delete[] image; + rip->SetFileName(filename); + rip->crc = crc; + return rip; } while (unzGoToNextFile(file) == UNZ_OK); @@ -877,4 +894,3 @@ BOOL Rip::SaveRip(const CHAR* filename) return 0; } - diff --git a/core/rip/Rip.h b/core/rip/Rip.h index 686629d..61bae5c 100644 --- a/core/rip/Rip.h +++ b/core/rip/Rip.h @@ -52,6 +52,14 @@ class Rip : public Peripheral BOOL SaveRip(const CHAR* filename); + const CHAR* GetFileName() { + return this->filename; + } + + UINT32 GetCRC() { + return this->crc; + } + private: Rip(UINT32 systemID); //Rip(UINT32 systemID, const CHAR* nme, const CHAR* prducer, const CHAR* yr); @@ -59,6 +67,10 @@ class Rip : public Peripheral void AddPeripheralUsage(const CHAR* periphName, PeripheralCompatibility usage); static Rip* LoadCartridgeConfiguration(const CHAR* cfgFile, UINT32 crc); + void SetFileName(const CHAR* fname) { + strncpy(this->filename, fname, sizeof(this->filename)); + } + UINT32 targetSystemID; CHAR* producer; CHAR* year; @@ -68,6 +80,8 @@ class Rip : public Peripheral PeripheralCompatibility peripheralUsages[MAX_PERIPHERALS]; UINT32 peripheralCount; + CHAR filename[MAX_PATH]; + UINT32 crc; }; #endif diff --git a/core/rip/knowncarts.cfg b/core/rip/knowncarts.cfg index 809e66e..b7aa37f 100644 --- a/core/rip/knowncarts.cfg +++ b/core/rip/knowncarts.cfg @@ -1,6 +1,9 @@ D7C78754:Info:4-TRIS (GPL):Joseph Zbiciak:2000 D7C78754:ROM:5000:2000:16 +B91488E2:Info:4-TRIS (GPL):Joseph Zbiciak:2001 +B91488E2:ROM:5000:2000:16 + A60E25FC:Info:ABPA Backgammon:Mattel:1978 A60E25FC:ROM:5000:1000:16 @@ -52,6 +55,9 @@ C047D487:ROM:4800:2000:16 B03F739B:Info:Blockade Runner:Interphase:1983 B03F739B:ROM:5000:2000:16 +4728C3BD:Info:Blow Out:Mattel:1983 +4728C3BD:ROM:5000:1000:16 + 515E1D7E:Info:Body Slam Super Pro Wrestling:Mattel:1988 515E1D7E:ROM:5000:2000:16 515E1D7E:ROM:9000:2000:16 @@ -78,6 +84,9 @@ AB87C16F:ROM:5000:1000:16 43806375:Info:BurgerTime!:Mattel:1982 43806375:ROM:5000:2000:16 +C92BAAE8:Info:BurgerTime! - New Levels Hack:David Harley:2002 +C92BAAE8:ROM:5000:2000:16 + FA492BBD:Info:Buzz Bombers:Mattel:1982 FA492BBD:ROM:5000:2000:16 @@ -98,6 +107,10 @@ D5363B8C:ROM:6000:2000:16 0BF464C6:ROM:5000:2000:16 0BF464C6:ROM:9000:2000:16 +47FDD8A8:Info:Choplifter:Mattel:1983 +47FDD8A8:ROM:5000:2000:16 +47FDD8A8:ROM:D000:2000:16 + 3289C8BA:Info:Commando:INTV:1987 3289C8BA:ROM:5000:2000:16 3289C8BA:ROM:9000:2000:16 @@ -144,6 +157,9 @@ AF8718A1:ROM:5000:1000:16 3B99B889:Info:Dreadnaught Factor, The:Activision:1983 3B99B889:ROM:5000:2000:16 +BF4D0E9B:Info:Dreadnaught Factor, The (Prototype):Activision:1983 +BF4D0E9B:ROM:5000:2000:16 + 20ACE89D:Info:Easter Eggs:Mattel:1981 20ACE89D:ROM:5000:1000:16 @@ -174,6 +190,9 @@ E573863A:ROM:5000:1E48:16 4B8C5932:Info:Happy Trails:Activision:1983 4B8C5932:ROM:5000:1000:16 +120B53A9:Info:Happy Trails (Overdump):Activision:1983 +120B53A9:ROM:5000:1000:16 + B6A3D4DE:Info:Hard Hat:Mattel:1979 B6A3D4DE:ROM:5000:2000:16 @@ -198,6 +217,11 @@ C83EEA4C:Info:Intellivision Test Cartridge and Baseball:Mattel:1978 C83EEA4C:ROM:5000:1000:16 C83EEA4C:ROM:7000:1000:16 +985A78ED:Info:IntyOS 0.2 Alpha:Arnauld Chevallier:2003 +985A78ED:ROM:5000:1000:16 +985A78ED:RAM:D000:1000:16 +985A78ED:RAM:F000:1000:16 + EE5F1BE2:Info:Jetsons, The - Ways With Words:Mattel:1983 EE5F1BE2:ROM:5000:2000:16 EE5F1BE2:ROM:D000:1000:16 @@ -223,9 +247,15 @@ A6840736:ROM:5000:2000:16 48D74D3C:Info:Las Vegas Roulette:Mattel:1979 48D74D3C:ROM:5000:1000:16 -19360442:Info:League of Light (Prototype):Activision:1983 +19360442:Info:League of Light (Prototype Alt1):Activision:1983 19360442:ROM:5000:2000:16 +75EE64F6:Info:League of Light (Prototype Alt2):Activision:1983 +75EE64F6:ROM:5000:2000:16 + +B4287B95:Info:League of Light (Prototype):Activision:1983 +B4287B95:ROM:5000:2000:16 + 2C5FD5FA:Info:Learning Fun I - Math Master Factor Fun:INTV:1987 2C5FD5FA:ROM:5000:2000:16 @@ -238,6 +268,10 @@ E00D1399:ROM:5000:2000:16 6B6E80EE:Info:Loco-Motion:Mattel:1982 6B6E80EE:ROM:5000:2000:16 +F3B0C759:Info:Magic Carousel:Mattel:1982 +F3B0C759:ROM:5000:2000:16 +F3B0C759:ROM:D000:2000:16 + 573B9B6D:Info:Masters of the Universe - The Power of He-Man!:Mattel:1983 573B9B6D:ROM:5000:2000:16 573B9B6D:ROM:D000:1000:16 @@ -260,11 +294,20 @@ E00E23E7:ROM:5000:006F:16 E806AD91:Info:Microsurgeon:Imagic:1982 E806AD91:ROM:4800:2000:16 +94096229:Info:Minehunter:Ryan Kinnen:2004 +94096229:ROM:5000:2000:16 + 9D57498F:Info:Mind Strike!:Mattel:1982 9D57498F:ROM:5000:2000:16 9D57498F:ROM:D000:1000:16 9D57498F:Peripheral:ECS:Required +6746607B:Info:Minotaur V1.1:Mattel:1981 +6746607B:ROM:5000:2000:16 + +5A4CE519:Info:Minotaur V2:Mattel:1981 +5A4CE519:ROM:5000:2000:16 + BD731E3C:Info:Minotaur:Mattel:1981 BD731E3C:ROM:5000:2000:16 @@ -283,11 +326,16 @@ BD731E3C:ROM:5000:2000:16 598662F2:Info:Mouse Trap:Coleco:1982 598662F2:ROM:5000:1000:16 -0B50A367:Info:Mr. Basic Meets Bits 'N Bytes:Mattel:1983 +0B50A367:Info:Mr. Basic Meets Bits 'N Bytes (Bad Dump):Mattel:1983 0B50A367:ROM:5000:2000:16 0B50A367:ROM:D000:1000:16 0B50A367:Peripheral:ECS:Required +BEF0B0C7:Info:Mr. Basic Meets Bits 'N Bytes:Mattel:1983 +BEF0B0C7:ROM:5000:2000:16 +BEF0B0C7:ROM:D000:1000:16 +BEF0B0C7:Peripheral:ECS:Required + DBAB54CA:Info:NASL Soccer:Mattel:1979 DBAB54CA:ROM:5000:1000:16 @@ -349,11 +397,20 @@ C7BB1B0E:ROM:5000:1000:16 8910C37A:Info:River Raid:Activision:1982-83 8910C37A:ROM:5000:2000:16 -1682D0B4:Info:Robot Rubble (Prototype):Activision:1983 -1682D0B4:ROM:5000:1000:16 +95466AD3:Info:River Raid V1 (Prototype):Activision:1982-83 +95466AD3:ROM:5000:2000:16 + +1682D0B4:Info:Robot Rubble V1 (Prototype) (Overdump):Activision:1983 +1682D0B4:ROM:5000:2000:16 + +7473916D:Info:Robot Rubble V1 (Prototype):Activision:1983 +7473916D:ROM:5000:2000:16 + +A5E28783:Info:Robot Rubble V2 (Prototype):Activision:1983 +A5E28783:ROM:5000:2000:16 243B0812:Info:Robot Rubble V3 (Prototype):Activision:1983 -243B0812:ROM:5000:1000:16 +243B0812:ROM:5000:2000:16 DCF4B15D:Info:Royal Dealer:Mattel:1981 DCF4B15D:ROM:5000:2000:16 @@ -361,6 +418,16 @@ DCF4B15D:ROM:5000:2000:16 47AA7977:Info:Safecracker:Imagic:1983 47AA7977:ROM:5000:2000:16 +6E0882E7:Info:SameGame and Robots:IntelligentVision:2005 +6E0882E7:ROM:5000:2000:16 +6E0882E7:ROM:D000:1000:16 +6E0882E7:ROM:F000:1000:16 + +12BA58D1:Info:SameGame and Robots (Original):Michael J Hayes:2004 +12BA58D1:ROM:5000:2000:16 +12BA58D1:ROM:D000:1000:16 +12BA58D1:ROM:F000:1000:16 + E221808C:Info:Santa's Helper:Mattel:1983 E221808C:ROM:5000:1000:16 @@ -374,6 +441,9 @@ E9E3F60D:Peripheral:ECS:Required E0F0D3DA:Info:Sewer Sam:Interphase:1983 E0F0D3DA:ROM:5000:2000:16 +A610406E:Info:Shape Escape:John Doherty:2005 +A610406E:ROM:5000:2000:16 + 2A4C761D:Info:Shark! Shark!:Mattel:1982 2A4C761D:ROM:5000:2000:16 @@ -449,6 +519,11 @@ F8EF3E5A:ROM:5000:2000:16 39D3B895:Info:Space Hawk:Mattel:1981 39D3B895:ROM:5000:1000:16 +E98B9163:Info:Space Shuttle:Mattel:1983 +E98B9163:ROM:5000:2000:16 +E98B9163:ROM:D000:3000:16 +E98B9163:Peripheral:Intellivoice:Optional + 3784DC52:Info:Space Spartans:Mattel:1981 3784DC52:ROM:5000:2000:16 3784DC52:Peripheral:Intellivoice:Optional @@ -470,6 +545,17 @@ B745C1CA:ROM:9000:2000:16 D5B0135A:Info:Star Wars - The Empire Strikes Back:Parker Bros:1983 D5B0135A:ROM:5000:1000:16 +A03EDF73:Info:Stack Em:Arnauld Chevallier:2004 +A03EDF73:ROM:4800:2000:16 + +66D396C0:Info:Stonix:Arnauld Chevallier:2004 +66D396C0:ROM:5000:2000:16 +66D396C0:ROM:D000:1000:16 +66D396C0:ROM:F000:1000:16 + +B119027D:Info:Stonix Beta 1.2:Arnauld Chevallier:2003 +B119027D:ROM:5000:2000:16 + 4830F720:Info:Street:Mattel:1981 4830F720:ROM:5000:1000:16 @@ -512,6 +598,9 @@ F3DF94E0:ROM:5000:2000:16 F3DF94E0:ROM:D000:1000:16 F3DF94E0:ROM:F000:1000:16 +D6495910:Info:Thin Ice (Prototype):Mattel:1983 +D6495910:ROM:5000:2000:16 + C1F1CA74:Info:Thunder Castle:Mattel:1982 C1F1CA74:ROM:5000:2000:16 C1F1CA74:ROM:D000:1000:16 @@ -603,3 +692,5 @@ A12C27E1:Peripheral:ECS:Required 15C65DC5:Info:Zaxxon:Coleco:1982 15C65DC5:ROM:5000:2000:16 +D89AEC27:Info:Zombie Marbles:John Doherty:2004 +D89AEC27:ROM:5000:2000:16 diff --git a/core/types.h b/core/types.h index 8059c36..ff471d5 100644 --- a/core/types.h +++ b/core/types.h @@ -49,7 +49,33 @@ typedef int BOOL; #endif #endif -// jeremiah sypult +#define SWAP32BIT(swap) ((((swap) << 24) & 0xFF000000) | \ + (((swap) << 8) & 0x00FF0000) | \ + (((swap) >> 8) & 0x0000FF00) | \ + (((swap) >> 24) & 0x000000FF)) + +#if !defined(__LITTLE_ENDIAN__) && defined(_WIN32) +#define __LITTLE_ENDIAN__ (1) +#endif + +#if defined(__LITTLE_ENDIAN__) +#define FOURCHAR(x) SWAP32BIT((UINT32)(x)) +#else +#define FOURCHAR(x) (x) +#endif + +#if defined(__GNUC__) +#define TYPEDEF_STRUCT_PACK(_x_) typedef struct __attribute__((__packed__)) _x_ +#define PACKED(_x_) _x_ __attribute__((__packed__)) +#elif defined(_MSC_VER) +#define TYPEDEF_STRUCT_PACK(_x_) __pragma(pack(1)) typedef struct _x_ __pragma(pack()) +#define PACKED(_x_) __pragma(pack(push,1)) _x_ __pragma(pack(pop)) +#else +#define TYPEDEF_STRUCT_PACK(_x_) _x_ +#define PACKED(_x_) _x_ +#warning pack macro is not supported on this compiler +#endif + #if defined( __MACH__ ) #include diff --git a/core/video/AY38900.cpp b/core/video/AY38900.cpp index 00c3bed..311bb6d 100755 --- a/core/video/AY38900.cpp +++ b/core/video/AY38900.cpp @@ -914,6 +914,60 @@ BOOL AY38900::mobsCollide(int mobNum0, int mobNum1) return FALSE; } +AY38900State AY38900::getState() +{ + AY38900State state = {0}; + + this->registers.getMemory(state.registers, 0, this->registers.getMemoryByteSize()); + state.backtab = this->backtab.getState(); + + state.inVBlank = this->inVBlank; + state.mode = this->mode; + state.previousDisplayEnabled = this->previousDisplayEnabled; + state.displayEnabled = this->displayEnabled; + state.colorStackMode = this->colorStackMode; + + state.borderColor = this->borderColor; + state.blockLeft = this->blockLeft; + state.blockTop = this->blockTop; + state.horizontalOffset = this->horizontalOffset; + state.verticalOffset = this->verticalOffset; + + for (int i = 0; i < 8; i++) { + state.mobs[i] = this->mobs[i].getState(); + } + + return state; +} + +void AY38900::setState(AY38900State state) +{ + this->registers.setMemory(state.registers, 0, this->registers.getMemoryByteSize()); + this->backtab.setState(state.backtab, state.backtab.image); + + this->inVBlank = state.inVBlank; + this->mode = state.mode; + this->previousDisplayEnabled = state.previousDisplayEnabled; + this->displayEnabled = state.displayEnabled; + this->colorStackMode = state.colorStackMode; + + this->borderColor = state.borderColor; + this->blockLeft = state.blockLeft; + this->blockTop = state.blockTop; + this->horizontalOffset = state.horizontalOffset; + this->verticalOffset = state.verticalOffset; + + for (int i = 0; i < 8; i++) { + mobs[i].setState(state.mobs[i]); + } + + this->colorModeChanged = TRUE; + this->bordersChanged = TRUE; + this->colorStackChanged = TRUE; + this->offsetsChanged = TRUE; + this->imageBufferChanged = TRUE; +} + /* void AY38900::renderRow(int rowNum) { UINT8 backTabPtr = (UINT8)(0x200+(rowNum*20)); @@ -990,4 +1044,3 @@ void AY38900::renderRow(int rowNum) { } } */ - diff --git a/core/video/AY38900.h b/core/video/AY38900.h index 0235d2a..c7b1820 100755 --- a/core/video/AY38900.h +++ b/core/video/AY38900.h @@ -15,6 +15,24 @@ #define AY38900_PIN_OUT_SR1 0 #define AY38900_PIN_OUT_SR2 1 +TYPEDEF_STRUCT_PACK( _AY38900State +{ + BackTabRAMState backtab; + MOBState mobs[8]; + INT32 horizontalOffset; + INT32 verticalOffset; + INT32 mode; + UINT16 registers[0x40]; + INT8 inVBlank; + INT8 previousDisplayEnabled; + INT8 displayEnabled; + INT8 colorStackMode; + UINT8 borderColor; + INT8 blockLeft; + INT8 blockTop; + UINT8 _pad[1]; +} AY38900State; ) + class AY38900 : public Processor, public VideoProducer { @@ -50,6 +68,9 @@ class AY38900 : public Processor, public VideoProducer */ void render(); + AY38900State getState(); + void setState(AY38900State state); + //registers AY38900_Registers registers; BackTabRAM backtab; @@ -89,18 +110,18 @@ class AY38900 : public Processor, public VideoProducer const static UINT8 stretch[]; const static UINT8 reverse[]; - MemoryBus* memoryBus; + MemoryBus* memoryBus; - UINT16 mobBuffers[8][128]; - MOB mobs[8]; - UINT8 backgroundBuffer[160*96]; + UINT16 mobBuffers[8][128]; + MOB mobs[8]; + UINT8 backgroundBuffer[160*96]; - UINT32* pixelBuffer; - UINT32 pixelBufferRowSize; + UINT32* pixelBuffer; + UINT32 pixelBufferRowSize; //memory listeners, for optimizations - ROM* grom; - GRAM* gram; + ROM* grom; + GRAM* gram; //state info BOOL inVBlank; @@ -246,4 +267,4 @@ class AY38900 : public Processor, public VideoProducer }; #endif -*/ \ No newline at end of file +*/ diff --git a/core/video/AY38900_Registers.h b/core/video/AY38900_Registers.h index 6a0dfad..be766c0 100644 --- a/core/video/AY38900_Registers.h +++ b/core/video/AY38900_Registers.h @@ -16,13 +16,22 @@ class AY38900_Registers : public RAM void poke(UINT16 location, UINT16 value); UINT16 peek(UINT16 location); + inline size_t getMemoryByteSize() { + return sizeof(memory); + } + void getMemory(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, memory + offset, size); + } + void setMemory(void* src, UINT16 offset, UINT16 size) { + memcpy(memory + offset, src, size); + } + private: AY38900_Registers(); void init(AY38900* ay38900); AY38900* ay38900; UINT16 memory[0x40]; - }; #endif diff --git a/core/video/BackTabRAM.cpp b/core/video/BackTabRAM.cpp index 312797d..01b8e1f 100644 --- a/core/video/BackTabRAM.cpp +++ b/core/video/BackTabRAM.cpp @@ -58,3 +58,22 @@ BOOL BackTabRAM::isDirty(UINT16 location) { return dirtyBytes[location-BACKTAB_LOCATION]; } +BackTabRAMState BackTabRAM::getState() +{ + BackTabRAMState state = {0}; + + state.RAMState = RAM::getState(state.image); + this->getImage(state.image, 0, this->getImageByteSize()); + + return state; +} + +void BackTabRAM::setState(BackTabRAMState state, UINT16* image) +{ + memset(this->dirtyBytes, TRUE, sizeof(this->dirtyBytes)); + RAM::setState(state.RAMState, NULL); + this->setImage(state.image, 0, this->getImageByteSize()); + + this->dirtyRAM = TRUE; + this->colorAdvanceBitsDirty = TRUE; +} diff --git a/core/video/BackTabRAM.h b/core/video/BackTabRAM.h index d280c18..73db433 100644 --- a/core/video/BackTabRAM.h +++ b/core/video/BackTabRAM.h @@ -7,6 +7,12 @@ #define BACKTAB_SIZE 0xF0 #define BACKTAB_LOCATION 0x200 +TYPEDEF_STRUCT_PACK( _BackTabRAMState +{ + RAMState RAMState; + UINT16 image[BACKTAB_SIZE]; +} BackTabRAMState; ) + class BackTabRAM : public RAM { @@ -22,6 +28,19 @@ class BackTabRAM : public RAM BOOL isDirty(UINT16 location); void markClean(); + inline size_t getImageByteSize() { + return size * sizeof(UINT16); + } + void getImage(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, image + offset, size); + } + void setImage(void* src, UINT16 offset, UINT16 size) { + memcpy(image + offset, src, size); + } + + BackTabRAMState getState(); + void setState(BackTabRAMState state, UINT16* image); + private: UINT16 image[BACKTAB_SIZE]; BOOL dirtyBytes[BACKTAB_SIZE]; diff --git a/core/video/GRAM.cpp b/core/video/GRAM.cpp index 9255754..3721f9a 100644 --- a/core/video/GRAM.cpp +++ b/core/video/GRAM.cpp @@ -57,3 +57,27 @@ BOOL GRAM::isCardDirty(UINT16 cardLocation) { return dirtyCards[cardLocation>>3]; } +RAMState GRAM::getState(UINT16* image) +{ + RAMState state = {0}; + + state = RAM::getState(NULL); + + if (image != NULL) { + this->getImage(image, 0, this->getImageByteSize()); + } + + return state; +} + +void GRAM::setState(RAMState state, UINT16* image) +{ + RAM::setState(state, NULL); + + if (image != NULL) { + this->setImage(image, 0, this->getImageByteSize()); + } + + memset(this->dirtyCards, TRUE, sizeof(this->dirtyCards)); + this->dirtyRAM = TRUE; +} diff --git a/core/video/GRAM.h b/core/video/GRAM.h index 2f506b3..47f613b 100644 --- a/core/video/GRAM.h +++ b/core/video/GRAM.h @@ -8,7 +8,6 @@ class GRAM : public RAM { - friend class AY38900; public: @@ -22,14 +21,25 @@ class GRAM : public RAM BOOL isDirty(); BOOL isCardDirty(UINT16 cardLocation); + inline size_t getImageByteSize() { + return size * sizeof(UINT16); + } + void getImage(void* dst, UINT16 offset, UINT16 size) { + memcpy(dst, image + offset, size); + } + void setImage(void* src, UINT16 offset, UINT16 size) { + memcpy(image + offset, src, size); + } + + RAMState getState(UINT16* image); + void setState(RAMState state, UINT16* image); + private: UINT16 image[GRAM_SIZE]; BOOL dirtyCards[GRAM_SIZE>>3]; BOOL dirtyRAM; static const UINT16 locations[16]; - }; #endif - diff --git a/core/video/MOB.cpp b/core/video/MOB.cpp index e3b316e..61d0a47 100644 --- a/core/video/MOB.cpp +++ b/core/video/MOB.cpp @@ -156,3 +156,49 @@ MOBRect* MOB::getBounds() { } return &boundingRectangle; } + +MOBState MOB::getState() +{ + MOBState state = {0}; + + state.xLocation = this->xLocation;; + state.yLocation = this->yLocation;; + state.foregroundColor = this->foregroundColor; + state.cardNumber = this->cardNumber; + state.collisionRegister = this->collisionRegister; + state.isGrom = this->isGrom; + state.isVisible = this->isVisible; + state.doubleWidth = this->doubleWidth; + state.doubleYResolution = this->doubleYResolution; + state.doubleHeight = this->doubleHeight; + state.quadHeight = this->quadHeight; + state.flagCollisions = this->flagCollisions; + state.horizontalMirror = this->horizontalMirror; + state.verticalMirror = this->verticalMirror; + state.behindForeground = this->behindForeground; + + return state; +} + +void MOB::setState(MOBState state) +{ + this->xLocation = state.xLocation; + this->yLocation = state.yLocation; + this->foregroundColor = state.foregroundColor; + this->cardNumber = state.cardNumber; + this->collisionRegister = state.collisionRegister; + this->isGrom = state.isGrom; + this->isVisible = state.isVisible; + this->doubleWidth = state.doubleWidth; + this->doubleYResolution = state.doubleYResolution; + this->doubleHeight = state.doubleHeight; + this->quadHeight = state.quadHeight; + this->flagCollisions = state.flagCollisions; + this->horizontalMirror = state.horizontalMirror; + this->verticalMirror = state.verticalMirror; + this->behindForeground = state.behindForeground; + + this->boundsChanged = TRUE; + this->shapeChanged = TRUE; + this->colorChanged = TRUE; +} diff --git a/core/video/MOB.h b/core/video/MOB.h index 1a693d0..2ca6c39 100644 --- a/core/video/MOB.h +++ b/core/video/MOB.h @@ -8,6 +8,25 @@ class AY38900; class AY38900_Registers; +TYPEDEF_STRUCT_PACK( _MOBState +{ + INT32 xLocation; + INT32 yLocation; + INT32 foregroundColor; + INT32 cardNumber; + UINT16 collisionRegister; + INT8 isGrom; + INT8 isVisible; + INT8 doubleWidth; + INT8 doubleYResolution; + INT8 doubleHeight; + INT8 quadHeight; + INT8 flagCollisions; + INT8 horizontalMirror; + INT8 verticalMirror; + INT8 behindForeground; +} MOBState; ) + class MOB { @@ -34,6 +53,9 @@ class MOB void markClean(); MOBRect* getBounds(); + MOBState getState(); + void setState(MOBState state); + INT32 xLocation; INT32 yLocation; INT32 foregroundColor; @@ -57,4 +79,3 @@ class MOB }; #endif - diff --git a/drivers/a5200/Atari5200.cpp b/drivers/a5200/Atari5200.cpp index d24c570..cdbd7e5 100755 --- a/drivers/a5200/Atari5200.cpp +++ b/drivers/a5200/Atari5200.cpp @@ -46,3 +46,14 @@ Atari5200::Atari5200() AddInputConsumer(&rightInput); } +BOOL Atari5200::SaveState(const CHAR* filename) +{ + printf("Atari5200::SaveState is not implemented\n"); + return FALSE; +} + +BOOL Atari5200::LoadState(const CHAR* filename) +{ + printf("Atari5200::LoadState is not implemented\n"); + return FALSE; +} diff --git a/drivers/a5200/Atari5200.h b/drivers/a5200/Atari5200.h index b5557a6..38e6243 100644 --- a/drivers/a5200/Atari5200.h +++ b/drivers/a5200/Atari5200.h @@ -18,6 +18,9 @@ class Atari5200 : public Emulator public: Atari5200(); + BOOL SaveState(const CHAR* filename); + BOOL LoadState(const CHAR* filename); + private: JoyPad leftInput; JoyPad rightInput; @@ -32,4 +35,3 @@ class Atari5200 : public Emulator }; #endif - diff --git a/drivers/intv/ECS.cpp b/drivers/intv/ECS.cpp index fbbc3f6..0ecf7fe 100644 --- a/drivers/intv/ECS.cpp +++ b/drivers/intv/ECS.cpp @@ -5,7 +5,7 @@ ECS::ECS() : Peripheral("Electronic Computer System", "ECS"), keyboard(2), - ramBank(0x0800, 0x4000, 8), + ramBank(ECS_RAM_SIZE, 0x4000, 8), uart(4, 0xE0, 8), psg2(0x00F0, &keyboard, &keyboard), bank0("ECS ROM #1", "ecs.bin", 0, 2, 0x1000, 0x2000), @@ -31,3 +31,21 @@ ECS::ECS() AddInputConsumer(&keyboard); } + +ECSState ECS::getState() +{ + ECSState state = {0}; + + state.ramState = ramBank.getState(state.ramImage); + state.uartState = uart.getState(NULL); + state.psg2State = psg2.getState(); + + return state; +} + +void ECS::setState(ECSState state) +{ + ramBank.setState(state.ramState, state.ramImage); + uart.setState(state.uartState, NULL); + psg2.setState(state.psg2State); +} diff --git a/drivers/intv/ECS.h b/drivers/intv/ECS.h index b0bcc9a..b4f6ec5 100644 --- a/drivers/intv/ECS.h +++ b/drivers/intv/ECS.h @@ -11,6 +11,16 @@ #include "core/types.h" #include "core/audio/AY38914.h" +#define ECS_RAM_SIZE 0x0800 + +TYPEDEF_STRUCT_PACK( _ECSState +{ + RAMState ramState; + UINT16 ramImage[ECS_RAM_SIZE]; + RAMState uartState; + AY38914State psg2State; +} ECSState; ) + class Intellivision; class ECS : public Peripheral @@ -21,6 +31,9 @@ class ECS : public Peripheral public: ECS(); + ECSState getState(); + void setState(ECSState state); + private: ECSKeyboard keyboard; diff --git a/drivers/intv/Intellivision.cpp b/drivers/intv/Intellivision.cpp index bedc54a..704c1b2 100755 --- a/drivers/intv/Intellivision.cpp +++ b/drivers/intv/Intellivision.cpp @@ -1,6 +1,31 @@ #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. @@ -11,17 +36,17 @@ Intellivision::Intellivision() player1Controller(0, "Hand Controller #1"), player2Controller(1, "Hand Controller #2"), psg(0x01F0, &player1Controller, &player2Controller), - RAM8bit(0x00F0, 0x0100, 8), - RAM16bit(0x0160, 0x0200, 16), + 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; + // 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); @@ -49,8 +74,8 @@ Intellivision::Intellivision() //add the GRAM AddRAM(&gram); - //add the backtab ram - AddRAM(&stic.backtab); + //add the backtab ram + AddRAM(&stic.backtab); //add the CPU AddProcessor(&cpu); @@ -72,3 +97,240 @@ Intellivision::Intellivision() 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; +} diff --git a/drivers/intv/Intellivision.h b/drivers/intv/Intellivision.h index e93954b..7c6587a 100644 --- a/drivers/intv/Intellivision.h +++ b/drivers/intv/Intellivision.h @@ -14,12 +14,17 @@ #include "core/video/AY38900.h" #include "core/audio/AY38914.h" +#define RAM8BIT_SIZE 0x00F0 +#define RAM16BIT_SIZE 0x0160 + class Intellivision : public Emulator { - public: Intellivision(); + BOOL SaveState(const CHAR* filename); + BOOL LoadState(const CHAR* filename); + private: //core processors CP1610 cpu; @@ -42,7 +47,6 @@ class Intellivision : public Emulator //the Intellivoice peripheral Intellivoice intellivoice; - }; #endif diff --git a/drivers/intv/Intellivoice.cpp b/drivers/intv/Intellivoice.cpp index 23470f1..a8b2dd5 100644 --- a/drivers/intv/Intellivoice.cpp +++ b/drivers/intv/Intellivoice.cpp @@ -1,3 +1,16 @@ #include "Intellivoice.h" +IntellivoiceState Intellivoice::getState() +{ + IntellivoiceState state = {0}; + + state.sp0256State = sp0256.getState(); + + return state; +} + +void Intellivoice::setState(IntellivoiceState state) +{ + sp0256.setState(state.sp0256State); +} diff --git a/drivers/intv/Intellivoice.h b/drivers/intv/Intellivoice.h index 2e37af4..ea66991 100644 --- a/drivers/intv/Intellivoice.h +++ b/drivers/intv/Intellivoice.h @@ -9,9 +9,13 @@ #include "core/audio/SP0256.h" #include "core/audio/AudioOutputLine.h" -class Intellivoice : public Peripheral +TYPEDEF_STRUCT_PACK( _IntellivoiceState { + SP0256State sp0256State; +} IntellivoiceState; ) +class Intellivoice : public Peripheral +{ public: Intellivoice() : Peripheral("Intellivoice", "Intellivoice") @@ -26,9 +30,11 @@ class Intellivoice : public Peripheral UINT32 getMemoryCount(); void getMemory(INT32 i, Memory** m); + IntellivoiceState getState(); + void setState(IntellivoiceState state); + private: SP0256 sp0256; - }; #endif diff --git a/sys/windows/Bliss.rc b/sys/windows/Bliss.rc index 5510e7e..dc77802 100644 --- a/sys/windows/Bliss.rc +++ b/sys/windows/Bliss.rc @@ -66,10 +66,13 @@ BEGIN POPUP "&File" BEGIN MENUITEM "&Open...\tCtrl+O", ID_FILE_OPEN - MENUITEM "&Reset", ID_FILE_RESET, INACTIVE + MENUITEM "&Reset\tShift+Ctrl+R", ID_FILE_RESET, INACTIVE MENUITEM "&Close", ID_FILE_CLOSE, INACTIVE MENUITEM SEPARATOR - MENUITEM "&Settings...", ID_SETTINGS + MENUITEM "Quick &Load State...\tShift+Ctrl+L", ID_FILE_QUICKLOAD, INACTIVE + MENUITEM "Quick &Save State...\tShift+Ctrl+S", ID_FILE_QUICKSAVE, INACTIVE + MENUITEM SEPARATOR + MENUITEM "Settings...", ID_SETTINGS MENUITEM SEPARATOR MENUITEM "E&xit\tAlt+F4", ID_APP_EXIT END @@ -110,7 +113,7 @@ BEGIN VALUE "LegalCopyright", "Released under the GNU Public License" VALUE "OriginalFilename", "Bliss.exe" VALUE "ProductName", "Bliss - The Intellivision Emulator" - VALUE "ProductVersion", "2.0.0" + VALUE "ProductVersion", "2.1.0" END END BLOCK "VarFileInfo" @@ -204,6 +207,9 @@ IDR_ACCELERATOR ACCELERATORS BEGIN VK_RETURN, ID_VIEW_FULLSCREENMODE, VIRTKEY, ALT, NOINVERT "O", ID_FILE_OPEN, VIRTKEY, CONTROL, NOINVERT + "L", ID_FILE_QUICKLOAD, VIRTKEY, SHIFT, CONTROL, NOINVERT + "S", ID_FILE_QUICKSAVE, VIRTKEY, SHIFT, CONTROL, NOINVERT + "R", ID_FILE_RESET, VIRTKEY, SHIFT, CONTROL, NOINVERT "P", ID_VIEW_PAUSE, VIRTKEY, ALT, NOINVERT END diff --git a/sys/windows/BlissApp.cpp b/sys/windows/BlissApp.cpp old mode 100755 new mode 100644 diff --git a/sys/windows/BlissAudioVideo.cpp b/sys/windows/BlissAudioVideo.cpp old mode 100755 new mode 100644 diff --git a/sys/windows/BlissAudioVideo.h b/sys/windows/BlissAudioVideo.h old mode 100755 new mode 100644 diff --git a/sys/windows/BlissMainFrame.cpp b/sys/windows/BlissMainFrame.cpp old mode 100755 new mode 100644 index b764b64..700a816 --- a/sys/windows/BlissMainFrame.cpp +++ b/sys/windows/BlissMainFrame.cpp @@ -24,6 +24,10 @@ BEGIN_MESSAGE_MAP(BlissMainFrame, CFrameWnd) ON_COMMAND(ID_FILE_OPEN, OnFileOpen) ON_COMMAND(ID_FILE_RESET, OnFileReset) ON_UPDATE_COMMAND_UI(ID_FILE_RESET, OnCheckMenuItems) + ON_COMMAND(ID_FILE_QUICKLOAD, OnFileQuickLoad) + ON_UPDATE_COMMAND_UI(ID_FILE_QUICKLOAD, OnCheckMenuItems) + ON_COMMAND(ID_FILE_QUICKSAVE, OnFileQuickSave) + ON_UPDATE_COMMAND_UI(ID_FILE_QUICKSAVE, OnCheckMenuItems) ON_COMMAND(ID_FILE_CLOSE, OnFileClose) ON_UPDATE_COMMAND_UI(ID_FILE_CLOSE, OnCheckMenuItems) ON_COMMAND(ID_SETTINGS, OnSettings) @@ -116,6 +120,8 @@ void BlissMainFrame::OnCheckMenuItems(CCmdUI* pCmdUI) switch (pCmdUI->m_nID) { case ID_FILE_RESET: case ID_FILE_CLOSE: + case ID_FILE_QUICKLOAD: + case ID_FILE_QUICKSAVE: pCmdUI->Enable(runState >= Paused); break; case ID_VIEW_PAUSE: @@ -325,6 +331,7 @@ BOOL BlissMainFrame::LoadRip(const CHAR* filename) strcpy(cfgFilename, theApp.StartUpPath); strcat(cfgFilename, "\\knowncarts.cfg"); currentRip = Rip::LoadZip(filename, cfgFilename); + delete[] cfgFilename; if (currentRip == NULL) return FALSE; @@ -381,6 +388,52 @@ void BlissMainFrame::OnFileReset() currentEmu->Reset(); } +void BlissMainFrame::OnFileQuickLoad() +{ + CHAR SAVFile[MAX_PATH] = {0}; + CHAR *tmp = NULL; + + if (currentRip) { + const CHAR* ROMFileName = currentRip->GetFileName(); + + strncpy(SAVFile, ROMFileName, sizeof(SAVFile)); + + tmp = strrchr(SAVFile, '.'); + + if (tmp) { + tmp++; + tmp[0] = 's'; + tmp[1] = 'a'; + tmp[2] = 'v'; + tmp[3] = '\0'; + currentEmu->LoadState(SAVFile); + } + } +} + +void BlissMainFrame::OnFileQuickSave() +{ + CHAR SAVFile[MAX_PATH] = {0}; + CHAR *tmp = NULL; + + if (currentRip) { + const CHAR* ROMFileName = currentRip->GetFileName(); + + strncpy(SAVFile, ROMFileName, sizeof(SAVFile)); + + tmp = strrchr(SAVFile, '.'); + + if (tmp) { + tmp++; + tmp[0] = 's'; + tmp[1] = 'a'; + tmp[2] = 'v'; + tmp[3] = '\0'; + currentEmu->SaveState(SAVFile); + } + } +} + void BlissMainFrame::OnFileClose() { runState = Stopped; @@ -882,6 +935,22 @@ LRESULT BlissMainFrame::OnNcHitTest(CPoint point) void BlissMainFrame::ShutDown() { + if (openDialog) { + delete openDialog; + openDialog = NULL; + } + + if (audioMixer) { + delete audioMixer; + audioMixer = NULL; + } + + if (videoBus) { + delete videoBus; + videoBus = NULL; + } + + ReleaseDirectInput(); ReleaseDirectSound(); ReleaseDirect3D(); diff --git a/sys/windows/BlissMainFrame.h b/sys/windows/BlissMainFrame.h old mode 100755 new mode 100644 index 36699e6..4323b83 --- a/sys/windows/BlissMainFrame.h +++ b/sys/windows/BlissMainFrame.h @@ -36,6 +36,8 @@ class BlissMainFrame : public CFrameWnd //menu item handlers afx_msg void OnFileOpen(); afx_msg void OnFileReset(); + afx_msg void OnFileQuickLoad(); + afx_msg void OnFileQuickSave(); afx_msg void OnFileClose(); afx_msg void OnSettings(); afx_msg void OnClose(); diff --git a/sys/windows/resource.h b/sys/windows/resource.h index 7d0ee07..a7c99d2 100644 --- a/sys/windows/resource.h +++ b/sys/windows/resource.h @@ -19,6 +19,8 @@ #define ID_SETTINGS 32775 #define ID_VIEW_FULLSCREENMODE 32776 #define ID_VIEW_PAUSE 32779 +#define ID_FILE_QUICKLOAD 32780 +#define ID_FILE_QUICKSAVE 32781 // Next default values for new objects //