// Apple 2 floppy disk support
//
// by James Hammons
-// (c) 2005-2018 Underground Software
+// (c) 2005-2019 Underground Software
//
// JLH = James Hammons <jlhamm@acm.org>
//
#include <string.h>
#include "apple2.h"
#include "crc32.h"
-#include "firmware.h"
+#include "fileio.h"
+#include "firmware/firmware.h"
#include "log.h"
#include "mmu.h"
#include "video.h" // For message spawning... Though there's probably a
// better approach than this!
+// For testing 13-sector disk FW
+//#define TEST13
+
// Useful enums
enum { IO_MODE_READ, IO_MODE_WRITE };
-// FloppyDrive class variable initialization
-
-uint8_t FloppyDrive::doSector[16] = {
- 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
-uint8_t FloppyDrive::poSector[16] = {
- 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
-uint8_t FloppyDrive::wozHeader[9] = "WOZ1\xFF\x0A\x0D\x0A";
-uint8_t FloppyDrive::wozHeader2[9] = "WOZ2\xFF\x0A\x0D\x0A";
-uint8_t FloppyDrive::standardTMAP[141] = {
- 0, 0, 0xFF, 1, 1, 1, 0xFF, 2, 2, 2, 0xFF, 3, 3, 3, 0xFF, 4, 4, 4, 0xFF,
- 5, 5, 5, 0xFF, 6, 6, 6, 0xFF, 7, 7, 7, 0xFF, 8, 8, 8, 0xFF, 9, 9, 9, 0xFF,
- 10, 10, 10, 0xFF, 11, 11, 11, 0xFF, 12, 12, 12, 0xFF, 13, 13, 13, 0xFF,
- 14, 14, 14, 0xFF, 15, 15, 15, 0xFF, 16, 16, 16, 0xFF, 17, 17, 17, 0xFF,
- 18, 18, 18, 0xFF, 19, 19, 19, 0xFF, 20, 20, 20, 0xFF, 21, 21, 21, 0xFF,
- 22, 22, 22, 0xFF, 23, 23, 23, 0xFF, 24, 24, 24, 0xFF, 25, 25, 25, 0xFF,
- 26, 26, 26, 0xFF, 27, 27, 27, 0xFF, 28, 28, 28, 0xFF, 29, 29, 29, 0xFF,
- 30, 30, 30, 0xFF, 31, 31, 31, 0xFF, 32, 32, 32, 0xFF, 33, 33, 33, 0xFF,
- 34, 34, 34, 0xFF, 0xFF, 0xFF
-};
-uint8_t FloppyDrive::bitMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
-uint8_t FloppyDrive::sequencerROM[256] = {
-0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
-0x2D, 0x38, 0x2D, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
-0x38, 0x28, 0xD8, 0x08, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B,
-0x48, 0x48, 0xD8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
-0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
-0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
-0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
-0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88,
-0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
-0x29, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
-0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB,
-0x59, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
-0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
-0x08, 0xE8, 0xD8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
-0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
-0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08
+// Misc. arrays (read only) that are needed
+
+static const uint8_t bitMask[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+static const uint8_t sequencerROM[256] = {
+ 0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // $00
+ 0x2D, 0x38, 0x2D, 0x38, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // $10
+ 0x38, 0x28, 0xD8, 0x08, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // $20
+ 0x48, 0x48, 0xD8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // $30
+ 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // $40
+ 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // $50
+ 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // $60
+ 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, // $70
+ 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // $80
+ 0x29, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // $90
+ 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // $A0
+ 0x59, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // $B0
+ 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // $C0
+ 0x08, 0xE8, 0xD8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // $D0
+ 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // $E0
+ 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08 // $F0
};
-char FloppyDrive::nameBuf[MAX_PATH];
-
-
-// Static in-line functions, for clarity & speed, mostly for reading values out
-// of the WOZ struct, which stores its data in LE; some for swapping variables
-static inline uint16_t Uint16LE(uint16_t v)
-{
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- return ((v & 0xFF) << 8) | ((v & 0xFF00) >> 8);
-#else
- return v;
+#if 1 // From UTA2E, but need to double check...
+static const uint8_t sequencerROM13[256] = {
+// vvvv (2, 4, 1, 3)
+ 0x18, 0x08, 0xD8, 0x18, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // $00
+ 0x28, 0x28, 0xD8, 0x28, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // $10
+ 0x38, 0x38, 0xD8, 0x38, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // $20
+ 0x48, 0x48, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // $30
+ 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // $40
+ 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // $50
+ 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // $60
+ 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, // $70
+ 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // $80
+ 0x09, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // $90
+ 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // $A0
+ 0x39, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // $B0
+ 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // $C0
+ 0x0D, 0xE8, 0x1D, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // $D0
+ 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // $E0
+ 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08 // $F0
+};
#endif
-}
-
-
-static inline uint32_t Uint32LE(uint32_t v)
-{
-#if SDL_BYTEORDER == SDL_BIG_ENDIAN
- return ((v & 0xFF) << 24) | ((v & 0xFF00) << 8)
- | ((v & 0xFF0000) >> 8) | ((v & 0xFF000000) >> 24);
-#else
- return v;
+#if 0
+static const uint8_t sequencerROM13[256] = {
+ 0x18, 0x08, 0xD8, 0x18, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18,
+ 0x98, 0x98, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98,
+ 0x38, 0x38, 0xD8, 0x38, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B,
+ 0xBD, 0xB8, 0xCD, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB,
+ 0x58, 0x58, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58,
+ 0xD9, 0xA0, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8,
+ 0x78, 0x78, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78,
+ 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8,
+ 0x28, 0x28, 0xD8, 0x28, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28,
+ 0x09, 0xA8, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8,
+ 0x48, 0x48, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48,
+ 0x39, 0xC8, 0xD9, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8,
+ 0x68, 0x68, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68,
+ 0x0D, 0xE8, 0x1D, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8,
+ 0x88, 0x88, 0xD8, 0xD8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88,
+ 0x4D, 0xE0, 0xDD, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x88, 0x08, 0x88, 0x08, 0x88, 0x08, 0x88, 0x08
+};
#endif
-}
-
+#if 0
+static const uint8_t sequencerROM13[256] = {
+ 0x88, 0x08, 0xB8, 0x88, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, 0x88, // $00
+ 0x98, 0x98, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // $10
+ 0xC8, 0xC8, 0xB8, 0xC8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xC9, 0xC9, 0xC9, 0xC9, 0xCB, 0xCB, 0xCB, 0xCB, // $20
+ 0xDD, 0xD8, 0x3D, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xD9, 0xD9, 0xD9, 0xD9, 0xDB, 0xDB, 0xDB, 0xDB, // $30
+ 0xA8, 0xA8, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // $40
+ 0xB9, 0x50, 0xB9, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, 0xB8, // $50
+ 0xE8, 0xE8, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // $60
+ 0xFD, 0xF8, 0xFD, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // $70
+ 0x48, 0x48, 0xB8, 0x48, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // $80
+ 0x09, 0x58, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // $90
+ 0x28, 0x28, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // $A0
+ 0xC9, 0x38, 0xB9, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, 0x38, // $B0
+ 0x68, 0x68, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // $C0
+ 0x0D, 0x78, 0x8D, 0x78, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // $D0
+ 0x18, 0x18, 0xB8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, // $E0
+ 0x2D, 0x70, 0xBD, 0x70, 0x0A, 0x0A, 0x0A, 0x0A,
+ 0x18, 0x08, 0x18, 0x08, 0x18, 0x08, 0x18, 0x08 // $F0
+};
+#endif
+static char nameBuf[MAX_PATH];
+// Static in-line functions, for clarity & speed, for swapping variables
static inline void Swap(uint8_t & a, uint8_t & b)
{
uint8_t t = a;
b = t;
}
-
static inline void Swap(uint32_t & a, uint32_t & b)
{
uint32_t t = a;
b = t;
}
-
static inline void Swap(bool & a, bool & b)
{
bool t = a;
b = t;
}
-
static inline void Swap(uint8_t * & a, uint8_t * & b)
{
uint8_t * t = a;
b = t;
}
-
-static inline void Swap(WOZ * & a, WOZ * & b)
-{
- WOZ * t = a;
- a = b;
- b = t;
-}
-
-
+//
// FloppyDrive class implementation...
-
-FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), ioHappened(false)
+//
+FloppyDrive::FloppyDrive(): motorOn(0), activeDrive(0), ioMode(IO_MODE_READ), ioHappened(false), diskImageReady(false)
{
phase[0] = phase[1] = 0;
headPos[0] = headPos[1] = 0;
trackLength[0] = trackLength[1] = 51200;
disk[0] = disk[1] = NULL;
- woz[0] = woz[1] = NULL;
diskSize[0] = diskSize[1] = 0;
diskType[0] = diskType[1] = DT_EMPTY;
imageDirty[0] = imageDirty[1] = false;
imageName[0][0] = imageName[1][0] = 0; // Zero out filenames
}
-
FloppyDrive::~FloppyDrive()
{
if (disk[0])
- delete[] disk[0];
+ free(disk[0]);
if (disk[1])
- delete[] disk[1];
+ free(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);
// Zero out filename, in case it doesn't load
imageName[driveNum][0] = 0;
+//prolly should load EjectImage() first, so we don't have to dick around with crap
+ uint8_t * buffer = ReadFile(filename, &diskSize[driveNum]);
- FILE * fp = fopen(filename, "rb");
-
- if (fp == NULL)
+ if (buffer == NULL)
{
WriteLog("FLOPPY: Failed to open image file '%s' for reading...\n", filename);
return false;
}
if (disk[driveNum])
- delete[] disk[driveNum];
+ free(disk[driveNum]);
- fseek(fp, 0, SEEK_END);
- diskSize[driveNum] = ftell(fp);
- fseek(fp, 0, SEEK_SET);
- disk[driveNum] = new uint8_t[diskSize[driveNum]];
- woz[driveNum] = (WOZ *)disk[driveNum];
- fread(disk[driveNum], 1, diskSize[driveNum], fp);
+ disk[driveNum] = buffer;
- fclose(fp);
-//printf("Read disk image: %u bytes.\n", diskSize);
+ diskImageReady = false;
DetectImageType(filename, driveNum);
strcpy(imageName[driveNum], filename);
+ diskImageReady = true;
WriteLog("FLOPPY: Loaded image '%s' for drive #%u.\n", filename, driveNum);
return true;
}
-
bool FloppyDrive::SaveImage(uint8_t driveNum/*= 0*/)
{
-// comment out for now...
-#if 0
// Various sanity checks...
if (driveNum > 1)
{
return false;
}
- if (!imageDirty[driveNum])
- {
- WriteLog("FLOPPY: No need to save unchanged image...\n");
- return false;
- }
-
- if (imageName[driveNum][0] == 0)
+ if (diskType[driveNum] == DT_EMPTY)
{
- WriteLog("FLOPPY: Attempted to save non-existant image!\n");
+ WriteLog("FLOPPY: No image in drive #%u to save\n", driveNum);
return false;
}
- // Finally, write the damn image
- FILE * fp = fopen(imageName[driveNum], "wb");
-
- if (fp == NULL)
+ if (!imageDirty[driveNum])
{
- WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
+ WriteLog("FLOPPY: No need to save unchanged image in drive #%u...\n", driveNum);
return false;
}
- fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
- fclose(fp);
-
- WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
-
- return true;
-#else
char * ext = strrchr(imageName[driveNum], '.');
if ((ext != NULL) && (diskType[driveNum] != DT_WOZ))
memcpy(ext, ".woz", 4);
- return SaveWOZ(driveNum);
-#endif
+ return SaveWOZ(imageName[driveNum], (WOZ2 *)disk[driveNum], diskSize[driveNum]);
}
-
bool FloppyDrive::SaveImageAs(const char * filename, uint8_t driveNum/*= 0*/)
{
strncpy(imageName[driveNum], filename, MAX_PATH);
return SaveImage(driveNum);
}
-
void FloppyDrive::CreateBlankImage(uint8_t driveNum/*= 0*/)
{
if (disk[driveNum] != NULL)
- delete disk[driveNum];
+ free(disk[driveNum]);
- InitWOZ(driveNum);
+ disk[driveNum] = InitWOZ(&diskSize[driveNum]);
diskType[driveNum] = DT_WOZ;
strcpy(imageName[driveNum], "newblank.woz");
SpawnMessage("New blank image inserted in drive %u...", driveNum);
}
-
void FloppyDrive::SwapImages(void)
{
-#if 0
-WriteLog("SwapImages BEFORE:\n");
-WriteLog("\tdisk[0]=%X, disk[1]=%X\n", disk[0], disk[1]);
-WriteLog("\twoz[0]=%X, woz[1]=%X\n", woz[0], woz[1]);
-WriteLog("\tdiskSize[0]=%X, diskSize[1]=%X\n", diskSize[0], diskSize[1]);
-WriteLog("\tdiskType[0]=%X, diskType[1]=%X\n", diskType[0], diskType[1]);
-WriteLog("\timageDirty[0]=%X, imageDirty[1]=%X\n", imageDirty[0], imageDirty[1]);
-WriteLog("\tphase[0]=%X, phase[1]=%X\n", phase[0], phase[1]);
-WriteLog("\theadPos[0]=%X, headPos[1]=%X\n", headPos[0], headPos[1]);
-WriteLog("\tcurrentPos[0]=%X, currentPos[1]=%X\n", currentPos[0], currentPos[1]);
-#endif
char imageNameTmp[MAX_PATH];
memcpy(imageNameTmp, imageName[0], MAX_PATH);
memcpy(imageName[1], imageNameTmp, MAX_PATH);
Swap(disk[0], disk[1]);
- Swap(woz[0], woz[1]);
Swap(diskSize[0], diskSize[1]);
Swap(diskType[0], diskType[1]);
Swap(imageDirty[0], imageDirty[1]);
Swap(headPos[0], headPos[1]);
Swap(currentPos[0], currentPos[1]);
SpawnMessage("Drive 0: %s...", imageName[0]);
-#if 0
-WriteLog("SwapImages AFTER:\n");
-WriteLog("\tdisk[0]=%X, disk[1]=%X\n", disk[0], disk[1]);
-WriteLog("\twoz[0]=%X, woz[1]=%X\n", woz[0], woz[1]);
-WriteLog("\tdiskSize[0]=%X, diskSize[1]=%X\n", diskSize[0], diskSize[1]);
-WriteLog("\tdiskType[0]=%X, diskType[1]=%X\n", diskType[0], diskType[1]);
-WriteLog("\timageDirty[0]=%X, imageDirty[1]=%X\n", imageDirty[0], imageDirty[1]);
-WriteLog("\tphase[0]=%X, phase[1]=%X\n", phase[0], phase[1]);
-WriteLog("\theadPos[0]=%X, headPos[1]=%X\n", headPos[0], headPos[1]);
-WriteLog("\tcurrentPos[0]=%X, currentPos[1]=%X\n", currentPos[0], currentPos[1]);
-#endif
}
-
/*
-Need to add some type of error checking here, so we can report back on bad images, etc.
+Need to add some type of error checking here, so we can report back on bad images, etc. (basically, it does by returning DFT_UNKNOWN, but we could do better)
*/
void FloppyDrive::DetectImageType(const char * filename, uint8_t driveNum)
{
diskType[driveNum] = DFT_UNKNOWN;
- if (memcmp(disk[driveNum], wozHeader, 8) == 0)
+ uint8_t wozType = CheckWOZType(disk[driveNum], diskSize[driveNum]);
+
+ if (wozType > 0)
{
+ // Check WOZ integrity...
+ CheckWOZIntegrity(disk[driveNum], diskSize[driveNum]);
+
+ // If it's a WOZ type 1 file, upconvert it to type 2
+ if (wozType == 1)
+ {
+ uint32_t size;
+ uint8_t * buffer = UpconvertWOZ1ToWOZ2(disk[driveNum], diskSize[driveNum], &size);
+
+ free(disk[driveNum]);
+ disk[driveNum] = buffer;
+ diskSize[driveNum] = size;
+ WriteLog("FLOPPY: Upconverted WOZ type 1 to type 2...\n");
+ }
+
+ WriteLog("FLOPPY: OBT is %d\n", ((WOZ2 *)disk[driveNum])->optimalBitTmg);
diskType[driveNum] = DT_WOZ;
- /*bool r =*/ CheckWOZ(disk[driveNum], diskSize[driveNum], driveNum);
}
else if (diskSize[driveNum] == 143360)
{
diskType[driveNum] = DT_PRODOS;
else if ((strcasecmp(ext, ".do") == 0) || (strcasecmp(ext, ".dsk") == 0))
{
- // We assume this, but check for a PRODOS fingerprint. Trust, but
- // verify. ;-)
+ // We assume this, but check for a PRODOS fingerprint. Trust, but
+ // verify. ;-)
diskType[driveNum] = DT_DOS33;
uint8_t fingerprint[4][4] = {
// No, we don't nybblize anymore. But we should tell the user that the loading failed with a return value
WriteLog("FLOPPY: Detected image type %s...\n", (diskType[driveNum] == DT_DOS33 ?
- "DOS 3.3 image" : (diskType[driveNum] == DT_DOS33_HDR ?
- "DOS 3.3 image (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS image" : (diskType[driveNum] == DT_WOZ ? "WOZ image" : "unknown")))));
+ "DOS 3.3" : (diskType[driveNum] == DT_DOS33_HDR ?
+ "DOS 3.3 (headered)" : (diskType[driveNum] == DT_PRODOS ? "ProDOS" : (diskType[driveNum] == DT_WOZ ? "WOZ" : "unknown")))));
}
-
//
// Write a bitstream (source left justified to bit 7) to destination buffer.
// Writes 'bits' number of bits to 'dest', starting at bit position 'dstPtr',
// updating 'dstPtr' for the caller.
//
-void FloppyDrive::WriteBits(uint8_t * dest, uint8_t * src, uint16_t bits, uint16_t * dstPtr)
+void FloppyDrive::WriteBits(uint8_t * dest, const uint8_t * src, uint16_t bits, uint16_t * dstPtr)
{
for(uint16_t i=0; i<bits; i++)
{
}
}
-
void FloppyDrive::WOZifyImage(uint8_t driveNum)
{
// hdr (21) + nybbles (343) + footer (48) = 412 bytes per sector
// let's go back to what we had, and see what happens :-)
// [still need to expand them back to what they were]
- uint8_t ff10[2] = { 0xFF, 0x00 };
+ const uint8_t ff10[2] = { 0xFF, 0x00 };
uint8_t addressHeader[14] = {
0xD5, 0xAA, 0x96, 0xFF, 0xFE, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0xDE, 0xAA, 0xEB };
- uint8_t sectorHeader[3] = { 0xD5, 0xAA, 0xAD };
- uint8_t footer[3] = { 0xDE, 0xAA, 0xEB };
- uint8_t diskbyte[0x40] = {
+ const uint8_t sectorHeader[3] = { 0xD5, 0xAA, 0xAD };
+ const uint8_t footer[3] = { 0xDE, 0xAA, 0xEB };
+ const uint8_t diskbyte[0x40] = {
0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF };
+ const uint8_t doSector[16] = {
+ 0x0, 0x7, 0xE, 0x6, 0xD, 0x5, 0xC, 0x4, 0xB, 0x3, 0xA, 0x2, 0x9, 0x1, 0x8, 0xF };
+ const uint8_t poSector[16] = {
+ 0x0, 0x8, 0x1, 0x9, 0x2, 0xA, 0x3, 0xB, 0x4, 0xC, 0x5, 0xD, 0x6, 0xE, 0x7, 0xF };
uint8_t tmpNib[343];
-// memcpy(tmpDisk, disk[driveNum], diskSize[driveNum]);
-// delete[] disk[driveNum];
+ // Save current image until we're done converting
uint8_t * tmpDisk = disk[driveNum];
- disk[driveNum] = NULL;//new uint8_t[diskSize[driveNum]];
// Set up track index...
-// memcpy(woz[driveNum]->tmap, standardTMAP, 141);
- InitWOZ(driveNum);
+ disk[driveNum] = InitWOZ(&diskSize[driveNum]);
+ WOZ2 & woz = *((WOZ2 *)disk[driveNum]);
// Upconvert data from DSK & friends format to WOZ tracks :-)
for(uint8_t trk=0; trk<35; trk++)
{
uint16_t dstBitPtr = 0;
- uint8_t * img = woz[driveNum]->track[trk].bits;
-//already done
-// memset(img, 0, 6646);
+ uint8_t * img = disk[driveNum] + (Uint16LE(woz.track[trk].startingBlock) * 512);
+//printf("Converting track %u: startingBlock=%u, %u blocks, img=%X\n", trk, Uint16LE(woz.track[trk].startingBlock), Uint16LE(woz.track[trk].blockCount), img);
// Write self-sync header bytes (16, should it be 64? Dunno.)
for(int i=0; i<64; i++)
// Write sector header (D5 AA AD)
WriteBits(img, sectorHeader, 3 * 8, &dstBitPtr);
-// uint8_t * bytes = disk[driveNum];
uint8_t * bytes = tmpDisk;
//Need to fix this so it writes the correct sector in the correct place *and* put the correct sector # into the header above as well. !!! FIX !!!
}
// Set the proper bit/byte lengths in the WOZ for this track
- woz[driveNum]->track[trk].bitCount = Uint16LE(dstBitPtr);
- woz[driveNum]->track[trk].byteCount = Uint16LE((dstBitPtr + 7) / 8);
+ woz.track[trk].bitCount = Uint16LE(dstBitPtr);
}
- delete[] tmpDisk;
+ // Finally, free the non-WOZ image now that we're done converting
+ free(tmpDisk);
}
-
const char * FloppyDrive::ImageName(uint8_t driveNum/*= 0*/)
{
// Set up a zero-length string for return value
return nameBuf;
}
-
void FloppyDrive::EjectImage(uint8_t driveNum/*= 0*/)
{
// Sanity check
WriteLog("FLOPPY: Ejected image file '%s' from drive %u...\n", imageName[driveNum], driveNum);
if (disk[driveNum])
- delete[] disk[driveNum];
+ free(disk[driveNum]);
disk[driveNum] = NULL;
- woz[driveNum] = NULL;
diskSize[driveNum] = 0;
diskType[driveNum] = DT_EMPTY;
imageDirty[driveNum] = false;
imageName[driveNum][0] = 0; // Zero out filenames
}
-
bool FloppyDrive::IsEmpty(uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
return (diskType[driveNum] == DT_EMPTY ? true : false);
}
-
bool FloppyDrive::IsWriteProtected(uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
return true;
}
- return (bool)woz[driveNum]->writeProtected;
+ WOZ2 & woz = *((WOZ2 *)disk[driveNum]);
+ return (bool)woz.writeProtected;
}
-
void FloppyDrive::SetWriteProtect(bool state, uint8_t driveNum/*= 0*/)
{
if (driveNum > 1)
return;
}
- woz[driveNum]->writeProtected = (uint8_t)state;
+ WOZ2 & woz = *((WOZ2 *)disk[driveNum]);
+ woz.writeProtected = (uint8_t)state;
}
-
int FloppyDrive::DriveLightStatus(uint8_t driveNum/*= 0*/)
{
int retval = DLS_OFF;
return retval;
}
-
void FloppyDrive::SaveState(FILE * file)
{
// Internal state vars
WriteLong(file, 0);
}
-
void FloppyDrive::LoadState(FILE * file)
{
// Eject images if they're loaded
imageDirty[0] = (fgetc(file) == 1 ? true : false);
fread(disk[0], 1, diskSize[0], file);
fread(imageName[0], 1, MAX_PATH, file);
- woz[0] = (WOZ *)disk[0];
}
diskSize[1] = ReadLong(file);
imageDirty[1] = (fgetc(file) == 1 ? true : false);
fread(disk[1], 1, diskSize[1], file);
fread(imageName[1], 1, MAX_PATH, file);
- woz[1] = (WOZ *)disk[1];
}
}
-
uint32_t FloppyDrive::ReadLong(FILE * file)
{
uint32_t r = 0;
return r;
}
-
void FloppyDrive::WriteLong(FILE * file, uint32_t l)
{
for(int i=0; i<4; i++)
}
}
-
-void FloppyDrive::WriteLongLE(FILE * file, uint32_t l)
-{
- for(int i=0; i<4; i++)
- {
- fputc(l & 0xFF, file);
- l >>= 8;
- }
-}
-
-
-void FloppyDrive::WriteWordLE(FILE * file, uint16_t w)
-{
- fputc(w & 0xFF, file);
- fputc((w >> 8) & 0xFF, file);
-}
-
-
-void FloppyDrive::WriteZeroes(FILE * file, uint32_t num)
-{
- for(uint32_t i=0; i<num; i++)
- fputc(0, file);
-}
-
-
// Memory mapped I/O functions + Logic State Sequencer
/*
According to Beneath Apple DOS, DOS checks the data register to see if it changes when spinning up a drive: "A sufficient delay should be provided to allow the motor time to come up to speed. Shugart recommends one second, but DOS is able to reduce this delay by watching the read latch until data starts to change." Which means, we can simulate an empty/off drive by leaving the data register alone.
*/
+uint64_t stepperTime = 0;
+bool seenReadSinceStep = false;
+uint16_t iorAddr;
void FloppyDrive::ControlStepper(uint8_t addr)
{
// $C0E0 - 7
The stepper motor has 4 phase solenoids (numbered 0-3) which corresponds to bits 1-2 of the address. Bit 0 tells the phase solenoid to either energize (1) or de-energize (0). By energizing the phase solenoids in ascending order, the stepper motor moves the head from a low numbered track to a higher numbered track; conversely, by energizing the solenoids in descending order, the stepper motor moves the head from a high numbered track to a lower one. Given that this is a mechanical device, it takes a certain amount of time for the drum in the stepper motor to move from place to place--though pretty much all software written for the Disk II takes this into account.
Tracks can apparently go from 0 to 79, though typically only 0 to 69 are usuable. Further, because of the limitations of the read/write head of the drive, not every track can be written to, so typically (about 99.99% of the time in my guesstimation) only every *other* track is written to (phases 0 and 2); some disks exist that have tracks written on phase 1 or 3, but these tend to be the exception rather than the rule.
+
+Taking into account the head slew time: ATM nothing seems to look at it, though it could be problematic as how we emulate it is different from how it actually works; namely, the emulator zaps the head to a new track instantly when the write to the phase happens while in the real thing, obviously this takes a non-zero amount of time. As such, none of the states where more than one phase solenoid is active at a time can be written so that they come on instantaneously; it would be fairly easy to write things that work on the real thing that don't on the emulator because of this. But most software (pretty much everything that I've ever seen) is pretty well behaved and this isn't an issue.
+
+If it ever *does* become a problem, doing the physical modeling of the head moving at a real velocity shouldn't be that difficult to do.
*/
+
+ // This is an array of stub positions crossed with solenoid energize
+ // patterns. The numbers represent how many quarter tracks the head will
+ // move given its current position and the pattern of energized solenoids.
+ // N.B.: Patterns for 11 & 13 haven't been filled in as I'm not sure how
+ // the stub(s) would react to those patterns. :-/
+ int16_t step[16][8] = {
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // [....]
+ { 0, -1, -2, 0, 0, 0, +2, +1 }, // [|...]
+ { +2, +1, 0, -1, -2, 0, 0, 0 }, // [.|..]
+ { +1, 0, -1, -2, -3, 0, +3, +2 }, // [||..]
+ { 0, 0, +2, +1, 0, -1, -2, 0 }, // [..|.]
+ { 0, -1, 0, +1, 0, -1, 0, +1 }, // [|.|.]
+ { +3, +2, +1, 0, -1, -2, -3, 0 }, // [.||.]
+ { +2, +1, 0, -1, -2, -3, 0, +3 }, // [|||.]
+ { -2, 0, 0, 0, +2, +1, 0, -1 }, // [...|]
+ { -1, -2, -3, 0, +3, +2, +1, 0 }, // [|..|]
+ { 0, +1, 0, -1, 0, +1, 0, -1 }, // [.|.|]
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // [||.|] ???
+ { -3, 0, +3, +2, +1, 0, -1, -2 }, // [..||]
+ { 0, 0, 0, 0, 0, 0, 0, 0 }, // [|.||] ???
+ { 0, +3, +2, +1, 0, -1, -2, -3 }, // [.|||]
+ { -1, +2, +1, 0, -1, -2, +1, 0 } // [||||]
+ };
+
// Sanity check
if (diskType[activeDrive] == DT_EMPTY)
return;
- // Convert phase solenoid number into a bit from 1 through 8:
+ // Convert phase solenoid number into a bit from 1 through 8 [1, 2, 4, 8]:
uint8_t phaseBit = 1 << ((addr >> 1) & 0x03);
// Set the state of the phase solenoid accessed using the phase bit
else
phase[activeDrive] &= ~phaseBit;
- // See if the new phase solenoid is energized, & move the stepper/head
- // appropriately.
- // N.B.: The head stub is located by bits 1 & 2 of the headPos variable
- uint8_t oldHeadPos = headPos[activeDrive];
- uint8_t nextUp = 1 << (((oldHeadPos >> 1) + 1) & 0x03);
- uint8_t nextDown = 1 << (((oldHeadPos >> 1) - 1) & 0x03);
+ int16_t oldHeadPos = headPos[activeDrive];
+ int16_t newStep = step[phase[activeDrive]][oldHeadPos & 0x07];
+ int16_t newHeadPos = (int16_t)headPos[activeDrive] + newStep;
+WriteLog("\nFLOPPY: oldHeadPos=%u, newHeadPos=%i, newStep=%i\n", oldHeadPos, newHeadPos, newStep);
- // We simulate cogging here by seeing if there's a valid up and/or down
- // position to go to. If both are valid, the head goes nowhere.
- if (phase[activeDrive] & nextUp)
- headPos[activeDrive] += (headPos[activeDrive] < 140 ? 2 : 0);
-
- if (phase[activeDrive] & nextDown)
- headPos[activeDrive] -= (headPos[activeDrive] > 0 ? 2 : 0);
+ // Sanity check
+ // N.B.: This is wrong for 3.5" disks
+ if ((newHeadPos >= 0) && (newHeadPos <= 140))
+ headPos[activeDrive] = (uint8_t)newHeadPos;
if (oldHeadPos != headPos[activeDrive])
{
- uint8_t newTIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
- float newBitLen = (newTIdx == 0xFF ? 51200.0f
- : Uint16LE(woz[activeDrive]->track[newTIdx].bitCount));
-
- uint8_t oldTIdx = woz[activeDrive]->tmap[oldHeadPos];
- float oldBitLen = (oldTIdx == 0xFF ? 51200.0f
- : Uint16LE(woz[activeDrive]->track[oldTIdx].bitCount));
+ WOZ2 & woz = *((WOZ2 *)disk[activeDrive]);
+ uint8_t newTIdx = woz.tmap[headPos[activeDrive]];
+ float newBitLen = (newTIdx == 0xFF
+ ? 51200.0f : Uint16LE(woz.track[newTIdx].bitCount));
+
+ uint8_t oldTIdx = woz.tmap[oldHeadPos];
+ float oldBitLen = (oldTIdx == 0xFF
+ ? 51200.0f : Uint16LE(woz.track[oldTIdx].bitCount));
+WriteLog("FLOPPY: Current pos pre: %u, ", currentPos[activeDrive]);
currentPos[activeDrive] = (uint32_t)((float)currentPos[activeDrive] * (newBitLen / oldBitLen));
+WriteLog("post: %u; newBitLen/old = %.1f/%.1f\n", currentPos[activeDrive], newBitLen, oldBitLen);
trackLength[activeDrive] = (uint16_t)newBitLen;
SpawnMessage("Stepping to track %u...", headPos[activeDrive] >> 2);
}
-WriteLog("FLOPPY: Stepper phase %d set to %s [%c%c%c%c] (track=%2.2f)\n", (addr >> 1) & 0x03, (addr & 0x01 ? "ON " : "off"), (phase[activeDrive] & 0x08 ? '|' : '.'), (phase[activeDrive] & 0x04 ? '|' : '.'), (phase[activeDrive] & 0x02 ? '|' : '.'), (phase[activeDrive] & 0x01 ? '|' : '.'), (float)headPos[activeDrive] / 4.0f);
+// only check the time since the phase was first set ON
+if (addr & 0x01)
+{
+ stepperTime = mainCPU.clock;
+ seenReadSinceStep = false;
+}
+WriteLog("FLOPPY: Stepper phase %d set to %s [%c%c%c%c] (track=%2.2f) [%u]\n", (addr >> 1) & 0x03, (addr & 0x01 ? "ON " : "off"), (phase[activeDrive] & 0x08 ? '|' : '.'), (phase[activeDrive] & 0x04 ? '|' : '.'), (phase[activeDrive] & 0x02 ? '|' : '.'), (phase[activeDrive] & 0x01 ? '|' : '.'), (float)headPos[activeDrive] / 4.0f, mainCPU.clock & 0xFFFFFFFF);
}
-
void FloppyDrive::ControlMotor(uint8_t addr)
{
WriteLog("FLOPPY: Turning drive motor %s\n", (motorOn ? "ON" : "off"));
}
-
void FloppyDrive::DriveEnable(uint8_t addr)
{
// $C0EA - B
WriteLog("FLOPPY: Selecting drive #%hhd\n", addr + 1);
}
-
/*
So for $C08C-F, we have two switches (Q6 & Q7) which combine to make four states ($C-D is off/on for Q6, $E-F is off/on for Q7).
*/
-
void FloppyDrive::SetShiftLoadSwitch(uint8_t state)
{
// $C0EC - D
slSwitch = state;
}
-
void FloppyDrive::SetReadWriteSwitch(uint8_t state)
{
// $C0EE - F
rwSwitch = state;
}
-
// MMIO: Reads from $C08x to $C0XX on even addresses
uint8_t FloppyDrive::DataRegister(void)
{
// Sanity check
if (diskType[activeDrive] != DT_EMPTY)
{
- uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
+ WOZ2 & woz = *((WOZ2 *)disk[activeDrive]);
+ uint8_t tIdx = woz.tmap[headPos[activeDrive]];
uint32_t bitLen = (tIdx == 0xFF ? 51200
- : Uint16LE(woz[activeDrive]->track[tIdx].bitCount));
+ : Uint16LE(woz.track[tIdx].bitCount));
SpawnMessage("%u:Reading $%02X from track %u, sector %u...",
activeDrive, dataRegister, headPos[activeDrive] >> 2, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f));
ioMode = IO_MODE_READ;
ioHappened = true;
+
+if ((seenReadSinceStep == false) && (slSwitch == false) && (rwSwitch == false) && ((iorAddr & 0x0F) == 0x0C))
+{
+ seenReadSinceStep = true;
+ WriteLog("%u:Reading $%02X from track %2.2f, sector %u (delta since seek: %lu cycles) [%u]...\n",
+ activeDrive, dataRegister, (float)headPos[activeDrive] / 4.0f, (uint32_t)(((float)currentPos[activeDrive] / (float)bitLen) * 16.0f), mainCPU.clock - stepperTime, mainCPU.clock & 0xFFFFFFFF);
+}
}
return dataRegister;
}
-
// MMIO: Writes from $C08x to $C0XX on odd addresses
void FloppyDrive::DataRegister(uint8_t data)
{
ioHappened = true;
}
-
/*
OFF switches ON switches
Switch Addr Func Addr Func
$5A: File structure damaged
*/
-
-//
-// This is used mainly to initialize blank disks and upconvert non-WOZ disks
-//
-void FloppyDrive::InitWOZ(uint8_t driveNum/*= 0*/)
-{
- // Sanity check
- if (disk[driveNum] != NULL)
- {
- WriteLog("FLOPPY: Attempted to initialize non-NULL WOZ structure\n");
- return;
- }
-
- diskSize[driveNum] = 256 + (35 * sizeof(WOZTrack));
- disk[driveNum] = new uint8_t[diskSize[driveNum]];
- woz[driveNum] = (WOZ *)disk[driveNum];
-
- // Zero out WOZ image in memory
- memset(woz[driveNum], 0, diskSize[driveNum]);
-
- // Set up header (leave CRC as 0 for now)
- memcpy(woz[driveNum]->magic, wozHeader, 8);
-
- // INFO header
- memcpy(woz[driveNum]->infoTag, "INFO", 4);
- woz[driveNum]->infoSize = Uint32LE(60);
- woz[driveNum]->infoVersion = 1;
- woz[driveNum]->diskType = 1;
- woz[driveNum]->writeProtected = 0;
- woz[driveNum]->synchronized = 0;
- woz[driveNum]->cleaned = 1;
- memset(woz[driveNum]->creator, ' ', 32);
- memcpy(woz[driveNum]->creator, "Apple2 emulator v1.0.0", 22);
-
- // TMAP header
- memcpy(woz[driveNum]->tmapTag, "TMAP", 4);
- woz[driveNum]->tmapSize = Uint32LE(160);
- memcpy(woz[driveNum]->tmap, standardTMAP, 141);
-
- // TRKS header
- memcpy(woz[driveNum]->trksTag, "TRKS", 4);
- woz[driveNum]->trksSize = Uint32LE(35 * sizeof(WOZTrack));
-
- for(int i=0; i<35; i++)
- {
- woz[driveNum]->track[i].bitCount = Uint16LE(51200);
- woz[driveNum]->track[i].byteCount = Uint16LE((51200 + 7) / 8);
- }
-
- // META header (how to handle? prolly with a separate pointer)
-}
-
-
-//
-// Do basic sanity checks on the passed in contents (file loaded elsewhere).
-// Returns true if successful, false on failure.
-//
-bool FloppyDrive::CheckWOZ(const uint8_t * wozData, uint32_t wozSize, uint8_t driveNum/*= 0*/)
-{
- // Hey! This reference works!! :-D
- WOZ & woz1 = *((WOZ *)wozData);
- woz[driveNum] = (WOZ *)wozData;
-
- // Basic sanity checking
- if (wozData == NULL)
- {
- WriteLog("FLOPPY: NULL pointer passed in to CheckWOZ()...\n");
- return false;
- }
-
- if (memcmp(woz1.magic, wozHeader, 8) != 0)
- {
- WriteLog("FLOPPY: Invalid WOZ header in file\n");
- return false;
- }
-
- uint32_t crc = CRC32(&wozData[12], wozSize - 12);
- uint32_t wozCRC = Uint32LE(woz1.crc32);
-
- if ((wozCRC != 0) && (wozCRC != crc))
- {
- WriteLog("FLOPPY: Corrupted data found in WOZ. CRC32: %08X, computed: %08X\n", wozCRC, crc);
- return false;
- }
- else if (wozCRC == 0)
- WriteLog("FLOPPY: Warning--WOZ file has no CRC...\n");
-
-#if 1
- WriteLog("Track map:\n");
- WriteLog(" 1 1 1 1 1 1 1 1\n");
- WriteLog("0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.\n");
- WriteLog("------------------------------------------------------------------------\n");
-
- for(uint8_t j=0; j<2; j++)
- {
- for(uint8_t i=0; i<72; i++)
- {
- char buf[64] = "..";
- buf[0] = buf[1] = '.';
-
- if (woz1.tmap[i] != 0xFF)
- sprintf(buf, "%02d", woz1.tmap[i]);
-
- WriteLog("%c", buf[j]);
- }
-
- WriteLog("\n");
- }
-
- WriteLog("\n1 1 2 2 2 2 2 2 2 2 2 2 3 3 3 3 3 3\n");
- WriteLog("8.,.9.,.0.,.1.,.2.,.3.,.4.,.5.,.6.,.7.,.8.,.9.,.0.,.1.,.2.,.3.,.4.,.5\n");
- WriteLog("---------------------------------------------------------------------\n");
-
- for(uint8_t j=0; j<2; j++)
- {
- for(uint8_t i=72; i<141; i++)
- {
- char buf[64] = "..";
-
- if (woz1.tmap[i] != 0xFF)
- sprintf(buf, "%02d", woz1.tmap[i]);
-
- WriteLog("%c", buf[j]);
- }
-
- WriteLog("\n");
- }
-
- WriteLog("\n");
-
- uint8_t numTracks = woz1.trksSize / sizeof(WOZTrack);
-
- // N.B.: Need to check the track[] to have this tell the correct track... Right now, it doesn't
- for(uint8_t i=0; i<numTracks; i++)
- {
- WriteLog("WOZ: Track %2.2f: %d bits (packed into %d bytes)\n", (float)i / 4.0f, woz1.track[i].bitCount, woz1.track[i].byteCount);
- }
-#endif
-
- WriteLog("FLOPPY: Well formed WOZ file found\n");
- return true;
-}
-
-
-bool FloppyDrive::SaveWOZ(uint8_t driveNum)
-{
- // Various sanity checks...
- if (driveNum > 1)
- {
- WriteLog("FLOPPY: Attempted to save image to drive #%u!\n", driveNum);
- return false;
- }
-
- if (diskType[driveNum] == DT_EMPTY)
- {
- WriteLog("FLOPPY: No image in drive #%u to save\n", driveNum);
- return false;
- }
-
- if (!imageDirty[driveNum])
- {
- WriteLog("FLOPPY: No need to save unchanged image in drive #%u...\n", driveNum);
- return false;
- }
-
- // Set up CRC32 before writing
- woz[driveNum]->crc32 = Uint32LE(CRC32(&disk[driveNum][12], diskSize[driveNum] - 12));
-
- // META header (skip for now) (actually, should be in the disk[] image already)
-
- // Finally, write the damn image
- FILE * fp = fopen(imageName[driveNum], "wb");
-
- if (fp == NULL)
- {
- WriteLog("FLOPPY: Failed to open image file '%s' for writing...\n", imageName[driveNum]);
- return false;
- }
-
- fwrite(disk[driveNum], 1, diskSize[driveNum], fp);
- fclose(fp);
-
- WriteLog("FLOPPY: Successfully wrote image file '%s'...\n", imageName[driveNum]);
-
- return true;
-}
-
-
// N.B.: The WOZ documentation says that the bitstream is normalized to 4µs.
// Which means on the //e that you would have to run it at that clock
// rate (instead of the //e clock rate 0.9799µs/cycle) to get the
// which will make the simulated drive run in the neighborhood of around
// 306 RPM. Should be close enough to get away with it. :-) (And it
// seems to run OK, for the most part.)
-
+//
+// According to EDD 4 the drive is running at 299.1 RPM... :-/
static bool logSeq = false;
+char sequence[1024];
//
// Logic State Sequencer & Data Register
//
static uint32_t prng = 1;
// Sanity checks
- if (diskType[activeDrive] == DT_EMPTY)
+ if (!diskImageReady)
+ return;
+ else if (diskType[activeDrive] == DT_EMPTY)
return;
else if (motorOn == false)
{
if (driveOffTimeout == 0)
return;
- else
- driveOffTimeout--;
+
+ driveOffTimeout--;
}
+ WOZ2 & woz = *((WOZ2 *)disk[activeDrive]);
+ uint8_t tIdx = woz.tmap[headPos[activeDrive]];
+ uint8_t * tdata = disk[activeDrive] + (Uint16LE(woz.track[tIdx].startingBlock) * 512);
+ // We have to divide the optimal bit timing by 4 because we only have a 0.5µs granularity here (with the doubling of the "cyclesToRun"). The OBT has a granularity of 0.125µs. Not sure how to fix that--have to separate the pulse handling from the sequencer?
+ // N.B.: Border Zone has an OBT of 28, when divides evenly by 4, but it still fails...
+ uint8_t pulseTiming = woz.optimalBitTmg / 4;
+
// It's x2 because the sequencer clock runs twice as fast as the CPU clock.
cyclesToRun *= 2;
while (cyclesToRun-- > 0)
{
- pulseClock = (pulseClock + 1) & 0x07;
+// pulseClock = (pulseClock + 1) & 0x07;
+// pulseClock = (pulseClock + 1) % 8;
+ pulseClock = (pulseClock + 1) % pulseTiming;
+// 7 doesn't work... Is that 3.5µs? Seems to be. Which means to get a 0.25µs granularity here, we need to double the # of cycles to run...
+// pulseClock = (pulseClock + 1) % 7;
if (pulseClock == 0)
{
uint16_t bytePos = currentPos[activeDrive] / 8;
uint8_t bitPos = currentPos[activeDrive] % 8;
- uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
+ window <<= 1;
if (tIdx != 0xFF)
{
- if (woz[activeDrive]->track[tIdx].bits[bytePos] & bitMask[bitPos])
+ if (tdata[bytePos] & bitMask[bitPos])
{
// According to Jim Sather (Understanding the Apple II),
// the Read Pulse, when it happens, is 1µs long, which is 2
- // sequencer clock pulses long.
- readPulse = 2;
+ // sequencer clock pulses long. (Not sure where, elsewhere, on pg. 9-29 it says it lasts one *sequencer* clock pulse.)
+ readPulse = 1;//2;
zeroBitCount = 0;
+ window |= 0x01;
}
else
zeroBitCount++;
-#if 0
- currentPos[activeDrive] = (currentPos[activeDrive] + 1) % Uint16LE(woz[activeDrive]->track[tIdx].bitCount);
- }
- else
- currentPos[activeDrive] = (currentPos[activeDrive] + 1) % 51200;
-#else
+//WriteLog("[%d]", tdata[bytePos] & bitMask[bitPos] ? 1 : 0);
}
-//this doesn't work reliably for some reason...
-//seems to work OK now...
+ readPulse = (window >> 1) & 0x01; // Read pulse delayed by about 5µs
currentPos[activeDrive] = (currentPos[activeDrive] + 1) % trackLength[activeDrive];
-#endif
// If we hit more than 2 zero bits in a row, simulate the disk head
// reader's Automatic Gain Control (AGC) turning itself up too high
/*
N.B.: Had to up this to 3 because Up N' Down had some weird sync bytes (FE10). May have to up it some more.
*/
- if ((zeroBitCount > 3) || (tIdx == 0xFF))
+// if ((zeroBitCount > 3) || (tIdx == 0xFF))
+ if (((window & 0x0F) == 0) || (tIdx == 0xFF))
{
if (prng & 0x00001)
{
// This PRNG is called the "Galois configuration".
prng ^= 0x24000;
- readPulse = 2;
+ readPulse = 1;//2;
}
prng >>= 1;
if (logSeq)
WriteLog("[%02X:%02X]%s", sequencerState, nextState, (chop == 15 ? "\n" : ""));
chop = (chop + 1) % 20;
+#ifndef TEST13
sequencerState = sequencerROM[nextState];
+#else
+// sequencerState = sequencerROM13[nextState];
+ sequencerState = sequencerROM[nextState];
+#endif
+
+#if 0
+uint32_t seqLen = strlen(sequence);
+
+if (seqLen + 7 > 1023)
+{
+ sequence[0] = 0;
+ seqLen = 0;
+}
+
+sprintf(&sequence[seqLen], "(%02X)%02X ", nextState, sequencerState);
+#endif
switch (sequencerState & 0x0F)
{
case 0x00:
- case 0x01:
- case 0x02:
- case 0x03:
- case 0x04:
- case 0x05:
- case 0x06:
- case 0x07:
- // CLR (clear data register)
+// case 0x01:
+// case 0x02:
+// case 0x03:
+// case 0x04:
+// case 0x05:
+// case 0x06:
+// case 0x07:
+ // CLR (clear data register; 0)
dataRegister = 0;
break;
case 0x08:
- case 0x0C:
+// case 0x0C:
// NOP (no operation)
break;
case 0x09:
dataRegister <<= 1;
//if (!stopWriting)
{
- uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
-
if (rwSwitch && (tIdx != 0xFF)
- && !woz[activeDrive]->writeProtected)
+ && !woz.writeProtected)
{
imageDirty[activeDrive] = true;
uint16_t bytePos = currentPos[activeDrive] / 8;
if (dataRegister & 0x80)
// Fill in the one, if necessary
- woz[activeDrive]->track[tIdx].bits[bytePos] |= bitMask[bitPos];
+ tdata[bytePos] |= bitMask[bitPos];
else
// Otherwise, punch in the zero
- woz[activeDrive]->track[tIdx].bits[bytePos] &= ~bitMask[bitPos];
+ tdata[bytePos] &= ~bitMask[bitPos];
#if 0
if (dumpDis || tripwire)
}
break;
case 0x0A:
- case 0x0E:
+// case 0x0E:
// SR (shift right write protect bit)
dataRegister >>= 1;
- dataRegister |= (woz[activeDrive]->writeProtected ? 0x80 : 0x00);
+ dataRegister |= (woz.writeProtected ? 0x80 : 0x00);
break;
case 0x0B:
- case 0x0F:
+// case 0x0F:
// LD (load data register from data bus)
dataRegister = cpuDataBus;
//if (!stopWriting)
{
- uint8_t tIdx = woz[activeDrive]->tmap[headPos[activeDrive]];
-
- if (rwSwitch && (tIdx != 0xFF)
- && !woz[activeDrive]->writeProtected)
+ if (rwSwitch && (tIdx != 0xFF) && !woz.writeProtected)
{
imageDirty[activeDrive] = true;
uint16_t bytePos = currentPos[activeDrive] / 8;
uint8_t bitPos = currentPos[activeDrive] % 8;
- woz[activeDrive]->track[tIdx].bits[bytePos] |= bitMask[bitPos];
+ tdata[bytePos] |= bitMask[bitPos];
#if 0
if (dumpDis || tripwire)
{
dataRegister <<= 1;
dataRegister |= 0x01;
break;
+ default:
+ // 1-7, $C, $E, & $F are all invalid opcodes
+ WriteLog("Invalid LSS state encountered! (opcode: $%X [full state: $%02X])\n", sequencerState & 0x0F, sequencerState);
}
if (readPulse > 0)
WriteLog("\n");
}
-
FloppyDrive floppyDrive[2];
static uint8_t SlotIOR(uint16_t address)
break;
}
+//temp, for debugging
+iorAddr = address;
// Even addresses return the data register, odd (we suppose) returns a
// floating bus read...
return (address & 0x01 ? ReadFloatingBus(0) : floppyDrive[0].DataRegister());
}
-
static void SlotIOW(uint16_t address, uint8_t byte)
{
uint8_t state = address & 0x0F;
// of FloppyDrive
static uint8_t SlotROM(uint16_t address)
{
+#ifndef TEST13
return diskROM[address];
+#else
+ return diskROM13[address];
+#endif
}
-
void InstallFloppy(uint8_t slot)
{
SlotData disk = { SlotIOR, SlotIOW, SlotROM, 0, 0, 0 };
InstallSlotHandler(slot, &disk);
}
-