#floppyImage1 = ./disks/temp.nib
#floppyImage1 = ./disks/temp.dsk
# Yes
+#floppyImage1 = ./disks/Gumball (Mr. Krac-Man and The Disk Jockey crack).dsk
+# Yes
+#floppyImage1 = ./disks/prince_of_persia_boot.dsk
+#floppyImage2 = ./disks/prince_of_persia_a.dsk
+# Yes
+#floppyImage1 = ./disks/Oregon Trail (Disk 1 of 2).dsk
+#floppyImage2 = ./disks/Oregon Trail (Disk 2 of 2).dsk
+# Yes
#floppyImage1 = ./disks/bt1_boot.dsk
# Yes
#floppyImage1 = ./disks/bt2_boot.dsk
-# Yes (but segfaults in the timer routine in the title screen--NB: Not anymore...)
-floppyImage1 = ./disks/bt3_boot_fixed.dsk
-floppyImage2 = ./disks/bt3_character_fixed.dsk
+# Yes
+#floppyImage1 = ./disks/bt3_boot_fixed.dsk
+#floppyImage2 = ./disks/bt3_character_fixed.dsk
# Yes
#floppyImage1 = ./disks/Sabotage.dsk
-# ??? (//c or //e w/128K required) (dumps to monitor)
+# Yes
#floppyImage1 = ./disks/airheart.dsk
# Yes
#floppyImage1 = ./disks/drol.dsk
#floppyImage1 = ./disks/karateka.dsk
# Yes
#floppyImage1 = ./disks/wolfenstein_dos32.nib
-# Yes, keys???
-#floppyImage1 = ./disks/MidnightMagic_etc.dsk
-# ??? Loads, then dumps to monitor (This is IIe or IIc only)
+# Yes, keys??? (joystick only)
+floppyImage1 = ./disks/MidnightMagic_etc.dsk
+# Yes
#floppyImage1 = ./disks/battle_chess_1.dsk
+#floppyImage2 = ./disks/battle_chess_2.dsk
# Yes
#floppyImage1 = ./disks/MoebiusI-1.dsk
#floppyImage2 = ./disks/MoebiusI-2.dsk
#floppyImage1 = ./disks/lode_runner.dsk
# Yes
#floppyImage1 = ./disks/championship_lode_runner.dsk
+# Yes
+#floppyImage1 = ./disks/championship_lode_runner.bin
# OpenGL filtering type: 1 - blurry, 0 - sharp
0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
char FloppyDrive::nameBuf[MAX_PATH];
+
// FloppyDrive class implementation...
FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), phase(0), track(0)
imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
}
+
FloppyDrive::~FloppyDrive()
{
if (disk[0])
delete[] disk[1];
}
+
bool FloppyDrive::LoadImage(const char * filename, uint8_t driveNum/*= 0*/)
{
WriteLog("FLOPPY: Attempting to load image '%s' in drive #%u.\n", filename, driveNum);
return true;
}
+
bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
return true;
}
+
bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
{
//WARNING: Buffer overflow possibility
return SaveImage(driveNum);
}
+
void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
{
if (disk[driveNum] != NULL)
SpawnMessage("New blank image inserted in drive %u...", driveNum);
}
+
void FloppyDrive::SwapImages(void)
{
uint8_t nybblizedImageTmp[232960];
SpawnMessage("Drive 0: %s...", imageName[0]);
}
+
void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
{
diskType[driveNum] = DT_UNKNOWN;
}
// Actually, it just might matter WRT to nybblyzing/denybblyzing
-// Here, we check for BT3
-//Nope, no change...
-//diskType[driveNum] = DT_PRODOS;
-
+ NybblizeImage(driveNum);
+ }
+ else if (diskSize[driveNum] == 143488)
+ {
+ diskType[driveNum] = DT_DOS33_HDR;
NybblizeImage(driveNum);
}
WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_NYBBLE ?
"Nybble image" : (diskType[driveNum] == DT_DOS33 ?
- "DOS 3.3 image" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown"))));
+ "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
+ "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : "unknown")))));
}
+
void FloppyDrive::NybblizeImage(uint8_t driveNum)
{
// Format of a sector is header (23) + nybbles (343) + footer (30) = 396
if (diskType[driveNum] == DT_DOS33)
bytes += (doSector[sector] * 256) + (trk * 256 * 16);
+ else if (diskType[driveNum] == DT_DOS33_HDR)
+ bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
else if (diskType[driveNum] == DT_PRODOS)
bytes += (poSector[sector] * 256) + (trk * 256 * 16);
else
}
}
+
void FloppyDrive::DenybblizeImage(uint8_t driveNum)
{
uint8_t decodeNybble[0x80] = {
if (diskType[driveNum] == DT_DOS33)
bytes += (doSector[sector] * 256) + (trk * 256 * 16);
+ else if (diskType[driveNum] == DT_DOS33_HDR)
+ bytes += (doSector[sector] * 256) + (trk * 256 * 16) + 128;
else if (diskType[driveNum] == DT_PRODOS)
bytes += (poSector[sector] * 256) + (trk * 256 * 16);
else
}
}
+
const char * FloppyDrive::GetImageName(uint8_t driveNum/*= 0*/)
{
// Set up a zero-length string for return value
return nameBuf;
}
+
void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
{
// Probably want to save a dirty image... ;-)
memset(nybblizedImage[driveNum], 0xFF, 232960); // Doesn't matter if 00s or FFs...
}
+
bool FloppyDrive::DriveIsEmpty(uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
return (imageName[driveNum][0] == 0 ? true : false);
}
+
bool FloppyDrive::DiskIsWriteProtected(uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
return writeProtected[driveNum];
}
+
void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
// return something if read mode...
}
+
void FloppyDrive::ControlMotor(uint8_t addr)
{
// $C0E8 - 9
motorOn = addr;
}
+
void FloppyDrive::DriveEnable(uint8_t addr)
{
// $C0EA - B
activeDrive = addr;
}
+
uint8_t FloppyDrive::ReadWrite(void)
{
SpawnMessage("%u:%sing %s track %u, sector %u...", activeDrive,
return diskByte;
}
+
uint8_t FloppyDrive::GetLatchValue(void)
{
// $C0ED
return latchValue;
}
+
void FloppyDrive::SetLatchValue(uint8_t value)
{
// $C0ED
latchValue = value;
}
+
void FloppyDrive::SetReadMode(void)
{
// $C0EE
ioMode = IO_MODE_READ;
}
+
void FloppyDrive::SetWriteMode(void)
{
// $C0EF
// STILL TO DO:
//
// - Figure out why it's losing samples (Bard's Tale) [DONE]
-// - Figure out why it's playing too fast
+// - Figure out why it's playing too fast [DONE]
//
#include "sound.h"
//#define DEBUG
//#define WRITE_OUT_WAVE
-// This is odd--seems to be working properly now! Maybe a bug in the SDL sound code?
-// Actually, it still doesn't sound right... Sounds too slow now. :-/
-// But then again, it's difficult to tell. Sometimes it slows waaaaaay down, but generally
-// seems to be OK other than that
-// Also, it could be that the discrepancy in pitch is due to the V65C02 and it's lack of
-// cycle accuracy...
-
//#define SAMPLE_RATE (44100.0)
#define SAMPLE_RATE (48000.0)
#define SAMPLES_PER_FRAME (SAMPLE_RATE / 60.0)
-// This works for AppleWin but not here... ??? WHY ???
-// ~ 21
#define CYCLES_PER_SAMPLE (1024000.0 / SAMPLE_RATE)
-// ~ 17 (lower pitched than above...!)
-// Makes sense, as this is the divisor for # of cycles passed
-//#define CYCLES_PER_SAMPLE (800000.0 / SAMPLE_RATE)
-// This seems about right, compared to AppleWin--but AW runs @ 1.024 MHz
-// 23 (1.024) vs. 20 (0.900)
-//#define CYCLES_PER_SAMPLE (900000.0 / SAMPLE_RATE)
-//nope, too high #define CYCLES_PER_SAMPLE (960000.0 / SAMPLE_RATE)
-//#define CYCLES_PER_SAMPLE 21
//#define SOUND_BUFFER_SIZE (8192)
#define SOUND_BUFFER_SIZE (32768)
{
if (soundInitialized)
{
-// SDL_PauseAudio(true);
SDL_PauseAudioDevice(device, 1);
-// SDL_CloseAudio();
SDL_CloseAudioDevice(device);
SDL_DestroyCond(conditional);
SDL_DestroyMutex(mutex);
uint32_t length = (uint32_t)length8 / 2;
//WriteLog("SDLSoundCallback(): filling buffer...\n");
- if (soundBufferPos < length) // The sound buffer is starved...
+ if (soundBufferPos < length)
{
+ // The sound buffer is starved...
for(uint32_t i=0; i<soundBufferPos; i++)
buffer[i] = soundBuffer[i];
// Fill buffer with last value
-// memset(buffer + soundBufferPos, (uint8_t)sample, length - soundBufferPos);
for(uint32_t i=soundBufferPos; i<length; i++)
buffer[i] = sample;
- soundBufferPos = 0; // Reset soundBufferPos to start of buffer...
+ // Reset soundBufferPos to start of buffer...
+ soundBufferPos = 0;
}
else
{
// Fill sound buffer with frame buffered sound
-// memcpy(buffer, soundBuffer, length);
for(uint32_t i=0; i<length; i++)
buffer[i] = soundBuffer[i];
//WriteLog("WriteSampleToBuffer(): SDL_mutexP(mutex2)\n");
SDL_mutexP(mutex2);
- // This should almost never happen, but...
+ // This should almost never happen, but, if it does...
while (soundBufferPos >= (SOUND_BUFFER_SIZE - 1))
{
//WriteLog("WriteSampleToBuffer(): Waiting for sound thread. soundBufferPos=%i, SOUNDBUFFERSIZE-1=%i\n", soundBufferPos, SOUND_BUFFER_SIZE-1);
}
-// Need some interface functions here to take care of flipping the
-// waveform at the correct time in the sound stream...
-
-/*
-Maybe set up a buffer 1 frame long (44100 / 60 = 735 bytes per frame)
-
-Hmm. That's smaller than the sound buffer 2048 bytes... (About 2.75 frames needed to fill)
-
-So... I guess what we could do is this:
-
--- Execute V65C02 for one frame. The read/writes at I/O address $C030 fill up the buffer
- to the current time position.
--- The sound callback function copies the pertinent area out of the buffer, resets
- the time position back (or copies data down from what it took out)
-*/
-
-void HandleBuffer(uint64_t elapsedCycles)
-{
- // Step 1: Calculate delta time
- uint64_t deltaCycles = elapsedCycles - lastToggleCycles;
-
- // Step 2: Calculate new buffer position
- uint32_t currentPos = (uint32_t)((double)deltaCycles / CYCLES_PER_SAMPLE);
-
- // Step 3: Make sure there's room for it
- // We need to lock since we touch both soundBuffer and soundBufferPos
- SDL_mutexP(mutex2);
-
- while ((soundBufferPos + currentPos) > (SOUND_BUFFER_SIZE - 1))
- {
- SDL_mutexV(mutex2); // Release it so sound thread can get it,
- SDL_mutexP(mutex); // Must lock the mutex for the cond to work properly...
- SDL_CondWait(conditional, mutex); // Sleep/wait for the sound thread
- SDL_mutexV(mutex); // Must unlock the mutex for the cond to work properly...
- SDL_mutexP(mutex2); // Re-lock it until we're done with it...
- }
-
- // Step 4: Backfill and adjust lastToggleCycles
- // currentPos is position from "zero" or soundBufferPos...
- currentPos += soundBufferPos;
-
-#ifdef WRITE_OUT_WAVE
- uint32_t sbpSave = soundBufferPos;
-#endif
- // Backfill with current toggle state
- while (soundBufferPos < currentPos)
- soundBuffer[soundBufferPos++] = sample;
-
-#ifdef WRITE_OUT_WAVE
- fwrite(&soundBuffer[sbpSave], sizeof(int16_t), currentPos - sbpSave, fp);
-#endif
-
- SDL_mutexV(mutex2);
- lastToggleCycles = elapsedCycles;
-}
-
-
-void ToggleSpeaker(uint64_t elapsedCycles)
+void ToggleSpeaker(void)
{
if (!soundInitialized)
return;
-// HandleBuffer(elapsedCycles);
speakerState = !speakerState;
sample = (speakerState ? amplitude[ampPtr] : -amplitude[ampPtr]);
}
-void AdjustLastToggleCycles(uint64_t elapsedCycles)
-{
- if (!soundInitialized)
- return;
-/*
-BOOKKEEPING
-
-We need to know the following:
-
- o Where in the sound buffer the base or "zero" time is
- o At what CPU timestamp the speaker was last toggled
- NOTE: we keep things "right" by advancing this number every frame, even
- if nothing happened! That way, we can keep track without having
- to detect whether or not several frames have gone by without any
- activity.
-
-How to do it:
-
-Every time the speaker is toggled, we move the base or "zero" time to the
-current spot in the buffer. We also backfill the buffer up to that point with
-the old toggle value. The next time the speaker is toggled, we measure the
-difference in time between the last time it was toggled (the "zero") and now,
-and repeat the cycle.
-
-We handle dead spots by backfilling the buffer with the current toggle value
-every frame--this way we don't have to worry about keeping current time and
-crap like that. So, we have to move the "zero" the right amount, just like
-in ToggleSpeaker(), and backfill only without toggling.
-*/
- HandleBuffer(elapsedCycles);
-}
-
-
void VolumeUp(void)
{
// Currently set for 16-bit samples
return ampPtr;
}
-/*
-HOW IT WORKS
-
-the main thread adds the amount of cpu time elapsed to samplebase. togglespeaker uses
-samplebase + current cpu time to find appropriate spot in buffer. it then fills the
-buffer up to the current time with the old toggle value before flipping it. the sound
-irq takes what it needs from the sound buffer and then adjusts both the buffer and
-samplebase back the appropriate amount.
-
-
-A better way might be as follows:
-
-Keep timestamp array of speaker toggle times. In the sound routine, unpack as many as will
-fit into the given buffer and keep going. Have the toggle function check to see if the
-buffer is full, and if it is, way for a signal from the interrupt that there's room for
-more. Can keep a circular buffer. Also, would need a timestamp buffer on the order of 2096
-samples *in theory* could toggle each sample
-
-Instead of a timestamp, just keep a delta. That way, don't need to deal with wrapping and
-all that (though the timestamp could wrap--need to check into that)
-
-Need to consider corner cases where a sound IRQ happens but no speaker toggle happened.
-
-If (delta > SAMPLES_PER_FRAME) then
-
-Here's the relevant cases:
-
-delta < SAMPLES_PER_FRAME -> Change happened within this time frame, so change buffer
-frame came and went, no change -> fill buffer with last value
-How to detect: Have bool bufferWasTouched = true when ToggleSpeaker() is called.
-Clear bufferWasTouched each frame.
-
-Two major cases here:
-
- o Buffer is touched on current frame
- o Buffer is untouched on current frame
-
-In the first case, it doesn't matter too much if the previous frame was touched or not,
-we don't really care except in finding the correct spot in the buffer to put our change
-in. In the second case, we need to tell the IRQ that nothing happened and to continue
-to output the same value.
-
-SO: How to synchronize the regular frame buffer with the IRQ buffer?
-
-What happens:
- Sound IRQ --> Every 1024 sample period (@ 44.1 KHz = 0.0232s)
- Emulation --> Render a frame --> 1/60 sec --> 735 samples
- --> sound buffer is filled
-
-Since the emulation is faster than the SIRQ the sound buffer should fill up
-prior to dumping it to the sound card.
-
-Problem is this: If silence happens for a long time then ToggleSpeaker is never
-called and the sound buffer has stale data; at least until soundBufferPos goes to
-zero and stays there...
-
-BUT this should be handled correctly by toggling the speaker value *after* filling
-the sound buffer...
-
-Still getting random clicks when running...
-(This may be due to the lock/unlock sound happening in ToggleSpeaker()...)
-*/
-