2013-07-06 04:37:13 +00:00
|
|
|
/*
|
2013-06-11 07:08:15 +00:00
|
|
|
* Apple // emulator for Linux: C portion of Disk ][ emulation
|
|
|
|
*
|
|
|
|
* Copyright 1994 Alexander Jean-Claude Bottema
|
|
|
|
* Copyright 1995 Stephen Lee
|
|
|
|
* Copyright 1997, 1998 Aaron Culliney
|
|
|
|
* Copyright 1998, 1999, 2000 Michael Deutschmann
|
|
|
|
*
|
|
|
|
* This software package is subject to the GNU General Public License
|
2013-07-06 04:37:13 +00:00
|
|
|
* version 2 or later (your choice) as published by the Free Software
|
2013-06-11 07:08:15 +00:00
|
|
|
* Foundation.
|
|
|
|
*
|
2013-07-06 04:37:13 +00:00
|
|
|
* THERE ARE NO WARRANTIES WHATSOEVER.
|
2013-06-11 07:08:15 +00:00
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
/*
|
|
|
|
* (De-)nibblizing routines sourced from AppleWin project.
|
|
|
|
*/
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
#include "common.h"
|
2014-04-07 04:49:02 +00:00
|
|
|
|
2014-11-15 19:14:01 +00:00
|
|
|
#if DISK_TRACING
|
2014-10-11 17:44:20 +00:00
|
|
|
static FILE *test_read_fp = NULL;
|
|
|
|
static FILE *test_write_fp = NULL;
|
|
|
|
#endif
|
|
|
|
|
2014-06-22 18:09:59 +00:00
|
|
|
extern uint8_t slot6_rom[256];
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
drive_t disk6;
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
static int stepper_phases = 0; // state bits for stepper magnet phases 0-3
|
|
|
|
static int skew_table_6_po[16] = { 0x00,0x08,0x01,0x09,0x02,0x0A,0x03,0x0B, 0x04,0x0C,0x05,0x0D,0x06,0x0E,0x07,0x0F }; // ProDOS order
|
|
|
|
static int skew_table_6_do[16] = { 0x00,0x07,0x0E,0x06,0x0D,0x05,0x0C,0x04, 0x0B,0x03,0x0A,0x02,0x09,0x01,0x08,0x0F }; // DOS order
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2015-10-11 20:21:45 +00:00
|
|
|
static uint8_t translate_table_6[0x40] = {
|
2013-07-06 04:37:13 +00:00
|
|
|
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
|
|
|
|
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
|
|
|
|
0xb4, 0xb5, 0xb6, 0xb7, 0xb9, 0xba, 0xbb, 0xbc,
|
|
|
|
0xbd, 0xbe, 0xbf, 0xcb, 0xcd, 0xce, 0xcf, 0xd3,
|
|
|
|
0xd6, 0xd7, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde,
|
|
|
|
0xdf, 0xe5, 0xe6, 0xe7, 0xe9, 0xea, 0xeb, 0xec,
|
|
|
|
0xed, 0xee, 0xef, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6,
|
|
|
|
0xf7, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff,
|
2014-12-28 21:07:13 +00:00
|
|
|
/*
|
2013-07-06 04:37:13 +00:00
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x00, 0x01,
|
|
|
|
0x80, 0x80, 0x02, 0x03, 0x80, 0x04, 0x05, 0x06,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x07, 0x08,
|
|
|
|
0x80, 0x80, 0x80, 0x09, 0x0a, 0x0b, 0x0c, 0x0d,
|
|
|
|
0x80, 0x80, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13,
|
|
|
|
0x80, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80, 0x80,
|
|
|
|
0x80, 0x80, 0x80, 0x1b, 0x80, 0x1c, 0x1d, 0x1e,
|
|
|
|
0x80, 0x80, 0x80, 0x1f, 0x80, 0x80, 0x20, 0x21,
|
|
|
|
0x80, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28,
|
|
|
|
0x80, 0x80, 0x80, 0x80, 0x80, 0x29, 0x2a, 0x2b,
|
|
|
|
0x80, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32,
|
|
|
|
0x80, 0x80, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38,
|
|
|
|
0x80, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f
|
2014-12-28 21:07:13 +00:00
|
|
|
*/
|
2013-07-06 04:37:13 +00:00
|
|
|
};
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2015-10-16 03:44:40 +00:00
|
|
|
static uint8_t rev_translate_table_6[0x80] = { 0x01 };
|
2015-10-11 20:21:45 +00:00
|
|
|
|
|
|
|
__attribute__((constructor(CTOR_PRIORITY_LATE)))
|
|
|
|
static void _initialize_reverse_translate(void) {
|
2015-10-13 06:11:05 +00:00
|
|
|
for (unsigned int i=0; i<0x40; i++) {
|
|
|
|
rev_translate_table_6[translate_table_6[i]-0x80] = i << 2;
|
2015-10-11 20:21:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-10 04:32:04 +00:00
|
|
|
static inline void cut_gz(char *name) {
|
|
|
|
size_t len = strlen(name);
|
|
|
|
if (len <= _GZLEN) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
*(name+len-_GZLEN) = '\0';
|
2014-03-26 05:11:01 +00:00
|
|
|
}
|
|
|
|
|
2015-10-10 04:32:04 +00:00
|
|
|
static inline bool is_gz(const char * const name) {
|
|
|
|
size_t len = strlen(name);
|
|
|
|
if (len <= _GZLEN) {
|
|
|
|
return false;
|
2014-03-26 05:11:01 +00:00
|
|
|
}
|
2015-10-10 04:32:04 +00:00
|
|
|
return strncmp(name+len-_GZLEN, DISK_EXT_GZ, _GZLEN) == 0;
|
2014-03-26 05:11:01 +00:00
|
|
|
}
|
|
|
|
|
2015-10-10 04:32:04 +00:00
|
|
|
static inline bool is_nib(const char * const name) {
|
|
|
|
size_t len = strlen(name);
|
|
|
|
if (len <= _NIBLEN) {
|
|
|
|
return false;
|
2014-03-26 05:11:01 +00:00
|
|
|
}
|
2015-10-10 04:32:04 +00:00
|
|
|
if (is_gz(name)) {
|
|
|
|
if (len <= _NIBLEN+_GZLEN) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
len -= _GZLEN;
|
2014-03-26 05:11:01 +00:00
|
|
|
}
|
2015-10-10 04:32:04 +00:00
|
|
|
return strncmp(name+len-_NIBLEN, DISK_EXT_NIB, _NIBLEN) == 0;
|
2014-03-26 05:11:01 +00:00
|
|
|
}
|
|
|
|
|
2015-10-10 04:32:04 +00:00
|
|
|
static inline bool is_po(const char * const name) {
|
2014-12-28 21:07:13 +00:00
|
|
|
size_t len = strlen( name );
|
2015-10-10 04:32:04 +00:00
|
|
|
if (len <= _POLEN) {
|
|
|
|
return false;
|
2014-10-11 17:44:20 +00:00
|
|
|
}
|
2015-10-10 04:32:04 +00:00
|
|
|
if (is_gz(name)) {
|
|
|
|
if (len <= _POLEN+_GZLEN) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
len -= _GZLEN;
|
2014-10-11 17:44:20 +00:00
|
|
|
}
|
2015-10-10 04:32:04 +00:00
|
|
|
return strncmp(name+len-_POLEN, DISK_EXT_PO, _POLEN) == 0;
|
2014-10-11 17:44:20 +00:00
|
|
|
}
|
|
|
|
|
2015-10-14 03:52:10 +00:00
|
|
|
#define SIXBIT_MASK 0x3F // 111111
|
|
|
|
#define SIXBIT_EXTRA_BYTES 0x56 // 86
|
|
|
|
#define SIXBIT_EXTRA_WRAP 0x53 // 86 + 86 + 83 == 255
|
|
|
|
#define SIXBIT_OFF_BEGIN (SIXBIT_EXTRA_BYTES<<1) // 0xAC
|
|
|
|
#define NUM_SIXBIT_NIBS (0x100 + SIXBIT_EXTRA_BYTES) // 256 + 86 == 342
|
2014-12-28 21:07:13 +00:00
|
|
|
|
2015-10-14 03:52:10 +00:00
|
|
|
#if DISK_TRACING
|
|
|
|
#define _DISK_TRACE_SIXBITNIBS() \
|
|
|
|
if (test_write_fp) { \
|
|
|
|
fprintf(test_write_fp, "SIXBITNIBS:\n"); \
|
|
|
|
for (unsigned int i=0; i<NUM_SIXBIT_NIBS+1; i++) { \
|
|
|
|
fprintf(test_write_fp, "%02X", work_buf[i]); \
|
|
|
|
} \
|
|
|
|
fprintf(test_write_fp, "\n"); \
|
|
|
|
}
|
|
|
|
#define _DISK_TRACE_XORNIBS() \
|
|
|
|
if (test_write_fp) { \
|
|
|
|
fprintf(test_write_fp, "XORNIBS:\n"); \
|
|
|
|
for (unsigned int i=0; i<NUM_SIXBIT_NIBS; i++) { \
|
|
|
|
fprintf(test_write_fp, "%02X", work_buf[i]); \
|
|
|
|
} \
|
|
|
|
fprintf(test_write_fp, "\n"); \
|
|
|
|
}
|
|
|
|
#define _DISK_TRACE_SECDATA() \
|
|
|
|
if (test_write_fp) { \
|
|
|
|
fprintf(test_write_fp, "SECTOR:\n"); \
|
|
|
|
for (unsigned int i = 0; i < 256; i++) { \
|
|
|
|
fprintf(test_write_fp, "%02X", out[i]); \
|
|
|
|
} \
|
|
|
|
fprintf(test_write_fp, "%s", "\n"); \
|
|
|
|
}
|
|
|
|
#else
|
|
|
|
#define _DISK_TRACE_SIXBITNIBS()
|
|
|
|
#define _DISK_TRACE_XORNIBS()
|
|
|
|
#define _DISK_TRACE_SECDATA()
|
|
|
|
#endif
|
|
|
|
|
|
|
|
static void nibblize_sector(const uint8_t * const src, uint8_t * const out) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_DISK("nibblize_sector");
|
2014-12-28 21:07:13 +00:00
|
|
|
|
2015-10-13 06:11:05 +00:00
|
|
|
uint8_t work_buf[NUM_SIXBIT_NIBS+1];
|
2014-12-28 21:07:13 +00:00
|
|
|
|
|
|
|
// Convert 256 8-bit bytes into 342 6-bit bytes
|
2015-10-13 06:11:05 +00:00
|
|
|
unsigned int counter = 0;
|
2015-10-14 03:52:10 +00:00
|
|
|
uint8_t offset = SIXBIT_OFF_BEGIN;
|
2015-10-13 06:11:05 +00:00
|
|
|
while (offset != 0x02) {
|
2015-10-14 03:52:10 +00:00
|
|
|
uint8_t value = ((src[offset] & 0x01) << 1) | ((src[offset] & 0x02) >> 1);
|
|
|
|
offset -= SIXBIT_EXTRA_BYTES;
|
|
|
|
value = (value << 2) | ((src[offset] & 0x01) << 1) | ((src[offset] & 0x02) >> 1);
|
|
|
|
offset -= SIXBIT_EXTRA_BYTES;
|
|
|
|
value = (value << 2) | ((src[offset] & 0x01) << 1) | ((src[offset] & 0x02) >> 1);
|
|
|
|
offset -= SIXBIT_EXTRA_WRAP;
|
|
|
|
work_buf[counter] = value << 2;
|
2015-10-13 06:11:05 +00:00
|
|
|
++counter;
|
2014-10-11 17:44:20 +00:00
|
|
|
}
|
2015-10-16 03:44:40 +00:00
|
|
|
assert(counter == SIXBIT_EXTRA_BYTES && "nibblizing counter about to overflow");
|
2015-10-14 03:52:10 +00:00
|
|
|
work_buf[counter-2] &= SIXBIT_MASK;
|
|
|
|
work_buf[counter-1] &= SIXBIT_MASK;
|
|
|
|
memcpy(&work_buf[counter], src, 0x100);
|
2014-12-28 21:07:13 +00:00
|
|
|
|
|
|
|
// XOR the entire data block with itself offset by one byte, creating a final checksum byte
|
2015-10-13 06:11:05 +00:00
|
|
|
uint8_t savedval = 0;
|
|
|
|
for (unsigned int i=0; i<NUM_SIXBIT_NIBS; i++) {
|
|
|
|
uint8_t prevsaved = savedval ^ work_buf[i];
|
|
|
|
savedval = work_buf[i];
|
|
|
|
work_buf[i] = prevsaved;
|
2014-11-15 19:14:01 +00:00
|
|
|
}
|
2015-10-13 06:11:05 +00:00
|
|
|
work_buf[NUM_SIXBIT_NIBS] = savedval;
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Convert the 6-bit bytes into disk bytes using a lookup table. A valid disk byte is a byte that has the high bit
|
|
|
|
// set, at least two adjacent bits set (excluding the high bit), and at most one pair of consecutive zero bits.
|
2015-10-13 06:11:05 +00:00
|
|
|
for (unsigned int i=0; i<NUM_SIXBIT_NIBS+1; i++) {
|
2015-10-14 03:52:10 +00:00
|
|
|
out[i] = translate_table_6[work_buf[i] >> 2];
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-14 03:52:10 +00:00
|
|
|
static void denibblize_sector(const uint8_t * const src, uint8_t * const out) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_DISK("denibblize_sector");
|
2014-03-26 05:11:01 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
uint8_t work_buf[NUM_SIXBIT_NIBS+1];
|
2014-03-26 05:11:01 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Convert disk bytes into 6-bit bytes
|
2015-10-13 06:11:05 +00:00
|
|
|
for (unsigned int i=0; i<(NUM_SIXBIT_NIBS+1); i++) {
|
|
|
|
work_buf[i] = rev_translate_table_6[src[i] & 0x7F];
|
2015-10-16 03:44:40 +00:00
|
|
|
assert(work_buf[i] != 0x1);
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
2015-10-14 03:52:10 +00:00
|
|
|
_DISK_TRACE_SIXBITNIBS();
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// XOR the entire data block with itself offset by one byte to undo checksumming
|
2015-10-13 06:11:05 +00:00
|
|
|
uint8_t savedval = 0;
|
|
|
|
for (unsigned int i=0; i<NUM_SIXBIT_NIBS; i++) {
|
|
|
|
work_buf[i] = savedval ^ work_buf[i];
|
|
|
|
savedval = work_buf[i];
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2015-10-14 03:52:10 +00:00
|
|
|
_DISK_TRACE_XORNIBS();
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Convert 342 6-bit bytes into 256 8-bit bytes
|
2015-10-14 03:52:10 +00:00
|
|
|
|
2015-10-13 06:11:05 +00:00
|
|
|
uint8_t *lowbitsptr = work_buf;
|
2015-10-14 03:52:10 +00:00
|
|
|
uint8_t *sectorbase = work_buf+SIXBIT_EXTRA_BYTES;
|
|
|
|
uint8_t offset = SIXBIT_OFF_BEGIN;
|
2015-10-13 06:11:05 +00:00
|
|
|
unsigned int counter = 0;
|
2015-10-14 03:52:10 +00:00
|
|
|
for (unsigned int i=0; i<SIXBIT_EXTRA_BYTES; i++) {
|
|
|
|
if (offset >= SIXBIT_OFF_BEGIN) {
|
|
|
|
out[offset] = (sectorbase[offset] & 0xFC) | ((lowbitsptr[i] & 0x80) >> 7) | ((lowbitsptr[i] & 0x40) >> 5);
|
2014-12-28 21:07:13 +00:00
|
|
|
++counter;
|
2015-10-13 06:11:05 +00:00
|
|
|
}
|
2015-10-14 03:52:10 +00:00
|
|
|
offset -= SIXBIT_EXTRA_BYTES;
|
|
|
|
out[offset] = (sectorbase[offset] & 0xFC) | ((lowbitsptr[i] & 0x20) >> 5) | ((lowbitsptr[i] & 0x10) >> 3);
|
2015-10-13 06:11:05 +00:00
|
|
|
++counter;
|
2015-10-14 03:52:10 +00:00
|
|
|
offset -= SIXBIT_EXTRA_BYTES;
|
|
|
|
out[offset] = (sectorbase[offset] & 0xFC) | ((lowbitsptr[i] & 0x08) >> 3) | ((lowbitsptr[i] & 0x04) >> 1);
|
2015-10-13 06:11:05 +00:00
|
|
|
++counter;
|
2015-10-14 03:52:10 +00:00
|
|
|
offset -= SIXBIT_EXTRA_WRAP;
|
2013-07-06 04:37:13 +00:00
|
|
|
}
|
2015-10-14 03:52:10 +00:00
|
|
|
assert(counter == 0x100 && "invalid bytes count");
|
|
|
|
assert(offset == 2 && "invalid bytes count");
|
|
|
|
_DISK_TRACE_SECDATA();
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
#define CODE44A(a) ((((a)>> 1) & 0x55) | 0xAA)
|
|
|
|
#define CODE44B(b) (((b) & 0x55) | 0xAA)
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-10-17 03:34:15 +00:00
|
|
|
static unsigned long nibblize_track(const uint8_t * const buf, int drive, uint8_t *output) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_DISK("nibblize_track");
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-10-17 18:36:54 +00:00
|
|
|
uint8_t * const begin_track = output;
|
2015-10-14 03:52:10 +00:00
|
|
|
|
2015-03-10 02:54:05 +00:00
|
|
|
#if CONFORMANT_TRACKS
|
|
|
|
// Write track-beginning gap containing 48 self-sync bytes
|
2014-12-28 21:07:13 +00:00
|
|
|
for (unsigned int i=0; i<48; i++) {
|
|
|
|
*(output++) = 0xFF;
|
|
|
|
}
|
2015-03-10 02:54:05 +00:00
|
|
|
#else
|
2015-10-04 18:51:29 +00:00
|
|
|
// NOTE : original apple2emul used X sync bytes here and disk loading becomes much faster at a cost of conformance
|
2015-03-10 02:54:05 +00:00
|
|
|
// for certain disk images. For resource-constrained mobile/wearable devices, this is prolly the right path.
|
2015-10-04 18:51:29 +00:00
|
|
|
for (unsigned int i=0; i<8; i++) {
|
2015-03-10 02:54:05 +00:00
|
|
|
*(output++) = 0xFF;
|
|
|
|
}
|
|
|
|
#endif
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
unsigned int sector = 0;
|
|
|
|
while (sector < 16) {
|
|
|
|
// --- Address field
|
|
|
|
|
|
|
|
// Prologue
|
|
|
|
*(output)++ = 0xD5;
|
|
|
|
*(output)++ = 0xAA;
|
|
|
|
*(output)++ = 0x96;
|
|
|
|
|
|
|
|
// Volume (4-and-4 encoded)
|
|
|
|
*(output)++ = CODE44A(DSK_VOLUME);
|
|
|
|
*(output)++ = CODE44B(DSK_VOLUME);
|
|
|
|
|
|
|
|
// Track (4-and-4 encoded)
|
|
|
|
int track = (disk6.disk[drive].phase>>1);
|
|
|
|
*(output)++ = CODE44A(track);
|
|
|
|
*(output)++ = CODE44B(track);
|
|
|
|
|
|
|
|
// Sector (4-and-4 encoded)
|
|
|
|
*(output)++ = CODE44A(sector);
|
|
|
|
*(output)++ = CODE44B(sector);
|
|
|
|
|
|
|
|
// Checksum (4-and-4 encoded)
|
|
|
|
uint8_t checksum = (DSK_VOLUME ^ track ^ sector);
|
|
|
|
*(output)++ = CODE44A(checksum);
|
|
|
|
*(output)++ = CODE44B(checksum);
|
|
|
|
|
|
|
|
// Epilogue
|
|
|
|
*(output)++ = 0xDE;
|
|
|
|
*(output)++ = 0xAA;
|
|
|
|
*(output)++ = 0xEB;
|
|
|
|
|
|
|
|
// Gap of 6 self-sync bytes
|
|
|
|
for (unsigned int i=0; i<6; i++) {
|
|
|
|
*(output++) = 0xFF;
|
|
|
|
}
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// --- Data field
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Prologue
|
|
|
|
*(output)++ = 0xD5;
|
|
|
|
*(output)++ = 0xAA;
|
|
|
|
*(output)++ = 0xAD;
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// 343 6-bit bytes of nibblized data + 6-bit checksum
|
|
|
|
int sec_off = 256 * disk6.disk[drive].skew_table[ sector ];
|
|
|
|
nibblize_sector(buf+sec_off, output);
|
|
|
|
output += NUM_SIXBIT_NIBS+1;
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Epilogue
|
|
|
|
*(output)++ = 0xDE;
|
|
|
|
*(output)++ = 0xAA;
|
|
|
|
*(output)++ = 0xEB;
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-03-10 02:54:05 +00:00
|
|
|
#if CONFORMANT_TRACKS
|
2014-12-28 21:07:13 +00:00
|
|
|
// Sector gap of 27 self-sync bytes
|
|
|
|
for (unsigned int i=0; i<27; i++) {
|
|
|
|
*(output++) = 0xFF;
|
|
|
|
}
|
2015-03-10 02:54:05 +00:00
|
|
|
#else
|
2015-10-04 18:51:29 +00:00
|
|
|
// NOTE : original apple2emul used X self-sync bytes here
|
|
|
|
for (unsigned int i=0; i<8; i++) {
|
2015-03-10 02:54:05 +00:00
|
|
|
*(output++) = 0xFF;
|
|
|
|
}
|
|
|
|
#endif
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
++sector;
|
|
|
|
}
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-10-17 03:34:15 +00:00
|
|
|
return output - begin_track;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-10-17 03:34:15 +00:00
|
|
|
static void denibblize_track(const uint8_t * const src, int drive, uint8_t * const dst) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_DISK("denibblize_track");
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Searches through the track data for each sector and decodes it
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-10-17 03:34:15 +00:00
|
|
|
const uint8_t * const trackimage = src;
|
2014-11-15 19:14:01 +00:00
|
|
|
#if DISK_TRACING
|
2014-12-28 21:07:13 +00:00
|
|
|
if (test_write_fp) {
|
|
|
|
fprintf(test_write_fp, "DSK OUT:\n");
|
|
|
|
}
|
2014-11-15 19:14:01 +00:00
|
|
|
#endif
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
unsigned int offset = 0;
|
|
|
|
int sector = -1;
|
2015-10-13 04:48:30 +00:00
|
|
|
|
2015-10-13 06:11:05 +00:00
|
|
|
// iterate over 2x sectors (accounting for header and data sections)
|
2015-10-13 04:48:30 +00:00
|
|
|
for (unsigned int sct2=0; sct2<(NUM_SECTORS<<1)+1; sct2++) {
|
2014-12-28 21:07:13 +00:00
|
|
|
uint8_t prologue[3] = {0,0,0}; // D5AA..
|
|
|
|
|
2015-10-13 06:11:05 +00:00
|
|
|
// Find the beginning of a header or data prologue
|
|
|
|
for (unsigned int i=0, idx=0; i<NIB_TRACK_SIZE; i++) { // FIXME: probably can change this to NIB_SEC_SIZE*2 ...
|
|
|
|
uint8_t byte = trackimage[offset];
|
|
|
|
if (idx || (byte == 0xD5)) {
|
|
|
|
prologue[idx] = byte;
|
|
|
|
++idx;
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
++offset;
|
2015-10-11 05:38:57 +00:00
|
|
|
if (offset >= disk6.disk[drive].track_width) {
|
2014-12-28 21:07:13 +00:00
|
|
|
offset = 0;
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2015-10-13 06:11:05 +00:00
|
|
|
if (idx >= 3) {
|
|
|
|
break;
|
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2015-10-13 06:11:05 +00:00
|
|
|
if (prologue[1] != 0xAA) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
#define SCTOFF 0x4
|
|
|
|
if (prologue[2] == 0x96) {
|
|
|
|
// found header prologue : extract sector
|
|
|
|
offset += SCTOFF;
|
|
|
|
if (offset >= disk6.disk[drive].track_width) {
|
|
|
|
RELEASE_LOG("WRAPPING PROLOGUE ...");
|
|
|
|
offset -= disk6.disk[drive].track_width;
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2015-10-13 06:11:05 +00:00
|
|
|
sector = ((trackimage[offset++] & 0x55) << 1);
|
|
|
|
sector |= (trackimage[offset++] & 0x55);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (UNLIKELY(prologue[2] != 0xAD)) {
|
|
|
|
RELEASE_LOG("OMG, found mid-track 0xD5 byte...");
|
|
|
|
continue;
|
|
|
|
}
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2015-10-13 06:11:05 +00:00
|
|
|
// found data prologue : copy and write data to sector
|
|
|
|
|
|
|
|
uint8_t work_buf[NUM_SIXBIT_NIBS+1];
|
|
|
|
for (unsigned int idx=0; idx<(NUM_SIXBIT_NIBS+1); idx++) {
|
|
|
|
work_buf[idx] = trackimage[offset];
|
|
|
|
++offset;
|
|
|
|
if (offset >= disk6.disk[drive].track_width) {
|
|
|
|
offset = 0;
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2015-10-16 04:23:21 +00:00
|
|
|
assert(sector >= 0 && sector < 16 && "invalid previous nibblization");
|
2015-10-13 06:11:05 +00:00
|
|
|
int sec_off = 256 * disk6.disk[drive].skew_table[ sector ];
|
|
|
|
denibblize_sector(work_buf, dst+sec_off);
|
|
|
|
sector = -1;
|
2014-10-11 17:44:20 +00:00
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
static size_t load_track_data(int drive, uint8_t * const out) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_DISK("load_track_data");
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2015-10-17 06:28:23 +00:00
|
|
|
size_t expected = 0;
|
|
|
|
uint8_t *buf = NULL;
|
2015-10-17 20:39:50 +00:00
|
|
|
if (disk6.disk[drive].nibblized) {
|
2014-12-28 21:07:13 +00:00
|
|
|
// .nib image
|
2015-10-17 06:28:23 +00:00
|
|
|
expected = NIB_TRACK_SIZE;
|
2015-10-17 20:39:50 +00:00
|
|
|
buf = out;
|
2015-10-17 06:28:23 +00:00
|
|
|
disk6.disk[disk6.drive].track_width = expected;
|
2014-12-28 21:07:13 +00:00
|
|
|
} else {
|
|
|
|
// .dsk, .do, .po images
|
2015-10-17 06:28:23 +00:00
|
|
|
expected = DSK_TRACK_SIZE;
|
|
|
|
uint8_t _buf[DSK_TRACK_SIZE] = { 0 };
|
|
|
|
buf = _buf; // !!!
|
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
const long track_pos = expected * (disk6.disk[drive].phase >> 1);
|
|
|
|
TEMP_FAILURE_RETRY(fseek(disk6.disk[drive].fp, track_pos, SEEK_SET));
|
2015-10-17 06:28:23 +00:00
|
|
|
|
|
|
|
size_t idx = 0;
|
|
|
|
do {
|
2015-10-17 20:39:50 +00:00
|
|
|
size_t ct = fread(buf+idx, 1, expected-idx, disk6.disk[drive].fp);
|
2015-10-17 06:28:23 +00:00
|
|
|
if (UNLIKELY(ct == 0)) {
|
2015-10-17 20:39:50 +00:00
|
|
|
if (ferror(disk6.disk[drive].fp)) {
|
2015-10-17 06:28:23 +00:00
|
|
|
// hopefully a transient error ...
|
|
|
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
|
usleep(10);
|
|
|
|
continue;
|
|
|
|
} else {
|
2015-10-17 20:39:50 +00:00
|
|
|
ERRLOG("OOPS, fatal error reading disk image %s at phase %d", disk6.disk[drive].file_name, disk6.disk[drive].phase);
|
2015-10-17 06:28:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2015-10-17 20:39:50 +00:00
|
|
|
ERRLOG("OOPS, EOF attemping to read disk image %s at phase %d", disk6.disk[drive].file_name, disk6.disk[drive].phase);
|
2015-10-17 06:28:23 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx += ct;
|
|
|
|
|
|
|
|
if (LIKELY(idx == expected)) {
|
|
|
|
break;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2015-10-17 06:28:23 +00:00
|
|
|
assert(idx < expected && "the world is not sane");
|
|
|
|
} while (1);
|
2014-10-11 17:44:20 +00:00
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
if (!disk6.disk[drive].nibblized) {
|
|
|
|
disk6.disk[disk6.drive].track_width = nibblize_track(buf, drive, out);
|
|
|
|
expected = disk6.disk[disk6.drive].track_width;
|
2015-03-10 02:54:05 +00:00
|
|
|
#if CONFORMANT_TRACKS
|
2015-10-17 20:39:50 +00:00
|
|
|
if (disk6.disk[drive].track_width != NI2_TRACK_SIZE) {
|
2014-12-28 21:07:13 +00:00
|
|
|
ERRLOG("Invalid dsk image creation...");
|
|
|
|
}
|
2015-10-17 20:39:50 +00:00
|
|
|
expected = 0;
|
|
|
|
#endif
|
2014-10-11 17:44:20 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
return expected;
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
static bool save_track_data(const uint8_t * const src, int drive) {
|
2015-10-04 21:21:28 +00:00
|
|
|
SCOPE_TRACE_DISK("save_track_data");
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2015-10-17 17:58:29 +00:00
|
|
|
size_t expected = 0;
|
2015-10-17 20:39:50 +00:00
|
|
|
const uint8_t *buf = NULL;
|
|
|
|
if (disk6.disk[drive].nibblized) {
|
2014-12-28 21:07:13 +00:00
|
|
|
// .nib image
|
2015-10-17 17:58:29 +00:00
|
|
|
expected = NIB_TRACK_SIZE;
|
2015-10-17 20:39:50 +00:00
|
|
|
buf = src;
|
2014-12-28 21:07:13 +00:00
|
|
|
} else {
|
|
|
|
// .dsk, .do, .po images
|
2015-10-17 17:58:29 +00:00
|
|
|
expected = DSK_TRACK_SIZE;
|
2015-10-17 20:39:50 +00:00
|
|
|
uint8_t _buf[DSK_TRACK_SIZE] = { 0 };
|
|
|
|
denibblize_track(src, drive, _buf);
|
2015-10-17 17:58:29 +00:00
|
|
|
buf = _buf; // !!!
|
2013-07-06 04:37:13 +00:00
|
|
|
}
|
2015-10-17 17:58:29 +00:00
|
|
|
|
|
|
|
const long track_pos = expected * (disk6.disk[disk6.drive].phase >> 1);
|
2015-10-17 20:39:50 +00:00
|
|
|
TEMP_FAILURE_RETRY(fseek(disk6.disk[drive].fp, track_pos, SEEK_SET));
|
2015-10-17 17:58:29 +00:00
|
|
|
|
|
|
|
size_t idx = 0;
|
|
|
|
do {
|
2015-10-17 20:39:50 +00:00
|
|
|
size_t ct = fwrite(buf+idx, 1, expected-idx, disk6.disk[drive].fp);
|
2015-10-17 17:58:29 +00:00
|
|
|
if (UNLIKELY(ct == 0)) {
|
2015-10-17 20:39:50 +00:00
|
|
|
if (ferror(disk6.disk[drive].fp)) {
|
2015-10-17 17:58:29 +00:00
|
|
|
// hopefully a transient error ...
|
|
|
|
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK) {
|
|
|
|
usleep(10);
|
|
|
|
continue;
|
|
|
|
} else {
|
2015-10-17 20:39:50 +00:00
|
|
|
ERRLOG("OOPS, fatal error reading disk image %s at phase %d", disk6.disk[drive].file_name, disk6.disk[drive].phase);
|
2015-10-17 17:58:29 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
} else {
|
2015-10-17 20:39:50 +00:00
|
|
|
ERRLOG("OOPS, EOF attemping to read disk image %s at phase %d", disk6.disk[drive].file_name, disk6.disk[drive].phase);
|
2015-10-17 17:58:29 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
idx += ct;
|
|
|
|
|
|
|
|
if (LIKELY(idx == expected)) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
assert(idx < expected && "the world is not sane");
|
|
|
|
} while (1);
|
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
TEMP_FAILURE_RETRY(fflush(disk6.disk[drive].fp));
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
disk6.disk[drive].track_dirty = false;
|
2014-12-28 21:07:13 +00:00
|
|
|
return true;
|
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
// Emulator hooks
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
GLUE_C_READ(disk_read_write_byte)
|
|
|
|
{
|
2015-10-17 20:23:49 +00:00
|
|
|
uint8_t value = 0x00;
|
2014-12-28 21:07:13 +00:00
|
|
|
do {
|
|
|
|
if (disk6.disk[disk6.drive].fp == NULL) {
|
2015-10-17 20:23:49 +00:00
|
|
|
////ERRLOG_THROTTLE("OOPS, attempt to read byte from NULL image in drive (%d)", disk6.drive+1);
|
2014-11-02 03:02:44 +00:00
|
|
|
break;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
if (!disk6.disk[disk6.drive].track_valid) {
|
2015-10-17 20:39:50 +00:00
|
|
|
if (!load_track_data(disk6.drive, disk6.disk[disk6.drive].track_image)) {
|
2014-12-28 21:07:13 +00:00
|
|
|
ERRLOG("OOPS, problem loading track data");
|
|
|
|
break;
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2015-10-17 20:39:50 +00:00
|
|
|
disk6.disk[disk6.drive].track_valid = true;
|
|
|
|
disk6.disk[disk6.drive].run_byte = 0;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2014-11-02 03:02:44 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
if (disk6.ddrw) {
|
|
|
|
if (disk6.disk[disk6.drive].is_protected) {
|
|
|
|
value = 0x00;
|
2015-10-17 20:39:50 +00:00
|
|
|
break; // Do not write if diskette is write protected
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
if (disk6.disk_byte < 0x96) {
|
|
|
|
ERRLOG("OOPS, attempting to write a non-nibblized byte");
|
|
|
|
value = 0x00;
|
|
|
|
break;
|
|
|
|
}
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2014-11-15 19:14:01 +00:00
|
|
|
#if DISK_TRACING
|
2014-11-02 03:02:44 +00:00
|
|
|
if (test_write_fp) {
|
2014-12-28 21:07:13 +00:00
|
|
|
fprintf(test_write_fp, "%02X", disk6.disk_byte);
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2014-10-11 17:44:20 +00:00
|
|
|
#endif
|
2014-12-28 21:07:13 +00:00
|
|
|
disk6.disk[disk6.drive].track_image[disk6.disk[disk6.drive].run_byte] = disk6.disk_byte;
|
|
|
|
disk6.disk[disk6.drive].track_dirty = true;
|
|
|
|
} else {
|
|
|
|
|
2015-01-11 20:03:07 +00:00
|
|
|
if (disk6.motor_off) { // ???
|
|
|
|
if (disk6.motor_off > 99) {
|
2014-12-28 21:07:13 +00:00
|
|
|
ERRLOG("OOPS, potential disk motor issue");
|
|
|
|
value = 0x00;
|
|
|
|
break;
|
|
|
|
} else {
|
2015-01-11 20:03:07 +00:00
|
|
|
disk6.motor_off++;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
value = disk6.disk[disk6.drive].track_image[disk6.disk[disk6.drive].run_byte];
|
|
|
|
#if DISK_TRACING
|
|
|
|
if (test_read_fp) {
|
|
|
|
fprintf(test_read_fp, "%02X", value);
|
2014-11-02 03:02:44 +00:00
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
} while (0);
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
++disk6.disk[disk6.drive].run_byte;
|
2015-10-11 05:38:57 +00:00
|
|
|
if (disk6.disk[disk6.drive].run_byte >= disk6.disk[disk6.drive].track_width) {
|
2013-07-06 04:37:13 +00:00
|
|
|
disk6.disk[disk6.drive].run_byte = 0;
|
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
#if DISK_TRACING
|
|
|
|
if ((disk6.disk[disk6.drive].run_byte % NIB_SEC_SIZE) == 0) {
|
|
|
|
if (disk6.ddrw) {
|
|
|
|
if (test_write_fp) {
|
|
|
|
fprintf(test_write_fp, "%s", "\n");
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (test_read_fp) {
|
|
|
|
fprintf(test_read_fp, "%s", "\n");
|
2013-07-06 04:37:13 +00:00
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return value;
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_phase)
|
|
|
|
{
|
2014-12-28 21:07:13 +00:00
|
|
|
ea &= 0xFF;
|
|
|
|
int phase = (ea>>1)&3;
|
|
|
|
int phase_bit = (1 << phase);
|
|
|
|
|
|
|
|
char *phase_str = NULL;
|
|
|
|
if (ea & 1) {
|
|
|
|
phase_str = "on ";
|
|
|
|
stepper_phases |= phase_bit;
|
|
|
|
} else {
|
|
|
|
phase_str = "off";
|
|
|
|
stepper_phases &= ~phase_bit;
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
#if DISK_TRACING
|
|
|
|
if (test_read_fp) {
|
|
|
|
fprintf(test_read_fp, "\ntrack %02X phases %X phase %d %s address $C0E%X\n", disk6.disk[disk6.drive].phase, stepper_phases, phase, phase_str, ea&0xF);
|
|
|
|
}
|
|
|
|
if (test_write_fp) {
|
|
|
|
fprintf(test_write_fp, "\ntrack %02X phases %X phase %d %s address $C0E%X\n", disk6.disk[disk6.drive].phase, stepper_phases, phase, phase_str, ea&0xF);
|
2013-07-06 04:37:13 +00:00
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
#endif
|
2013-07-06 04:37:13 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// Disk ][ Magnet causes stepping effect:
|
|
|
|
// - move only when the magnet opposite the cog is off
|
|
|
|
// - move in the direction of an adjacent magnet if one is on
|
|
|
|
// - do not move if both adjacent magnets are on
|
|
|
|
int direction = 0;
|
|
|
|
int cur_phase = disk6.disk[disk6.drive].phase;
|
|
|
|
if (stepper_phases & (1 << ((cur_phase + 1) & 3))) {
|
|
|
|
direction += 1;
|
|
|
|
}
|
|
|
|
if (stepper_phases & (1 << ((cur_phase + 3) & 3))) {
|
|
|
|
direction -= 1;
|
2013-07-06 04:37:13 +00:00
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
if (direction) {
|
|
|
|
if (disk6.disk[disk6.drive].track_dirty) {
|
2015-10-17 20:39:50 +00:00
|
|
|
save_track_data(disk6.disk[disk6.drive].track_image, disk6.drive);
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
disk6.disk[disk6.drive].track_valid = false;
|
|
|
|
disk6.disk[disk6.drive].phase += direction;
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
if (disk6.disk[disk6.drive].phase<0) {
|
|
|
|
disk6.disk[disk6.drive].phase=0;
|
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
if (disk6.disk[disk6.drive].phase>69) { // AppleWin uses 79 (extra tracks/phases)?
|
|
|
|
disk6.disk[disk6.drive].phase=69;
|
|
|
|
}
|
|
|
|
|
|
|
|
#if DISK_TRACING
|
|
|
|
if (test_read_fp) {
|
|
|
|
fprintf(test_read_fp, "NEW TRK:%d\n", (disk6.disk[disk6.drive].phase>>1));
|
|
|
|
}
|
|
|
|
if (test_write_fp) {
|
|
|
|
fprintf(test_write_fp, "NEW TRK:%d\n", (disk6.disk[disk6.drive].phase>>1));
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2015-01-31 21:57:10 +00:00
|
|
|
return ea == 0xE0 ? 0xFF : floating_bus_hibit(1);
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
2013-06-11 07:08:15 +00:00
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_motor_off)
|
|
|
|
{
|
2015-01-11 20:03:07 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &disk6.motor_time);
|
|
|
|
disk6.motor_off = 1;
|
2015-01-31 21:57:10 +00:00
|
|
|
return floating_bus_hibit(1);
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_motor_on)
|
|
|
|
{
|
2015-01-11 20:03:07 +00:00
|
|
|
clock_gettime(CLOCK_MONOTONIC, &disk6.motor_time);
|
|
|
|
disk6.motor_off = 0;
|
2015-01-31 21:57:10 +00:00
|
|
|
return floating_bus_hibit(1);
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_select_a)
|
|
|
|
{
|
2014-12-28 21:07:13 +00:00
|
|
|
disk6.drive = 0;
|
2015-01-31 21:57:10 +00:00
|
|
|
return floating_bus();
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_select_b)
|
|
|
|
{
|
2014-12-28 21:07:13 +00:00
|
|
|
disk6.drive = 1;
|
2015-01-31 21:57:10 +00:00
|
|
|
return floating_bus();
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_latch)
|
|
|
|
{
|
2013-07-06 04:37:13 +00:00
|
|
|
return disk6.drive;
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_prepare_in)
|
|
|
|
{
|
2013-07-06 04:37:13 +00:00
|
|
|
disk6.ddrw = 0;
|
2015-01-31 21:57:10 +00:00
|
|
|
return floating_bus_hibit(disk6.disk[disk6.drive].is_protected);
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_READ(disk_read_prepare_out)
|
|
|
|
{
|
2013-07-06 04:37:13 +00:00
|
|
|
disk6.ddrw = 1;
|
2015-01-31 21:57:10 +00:00
|
|
|
return floating_bus_hibit(1);
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
GLUE_C_WRITE(disk_write_latch)
|
|
|
|
{
|
2013-11-06 05:58:24 +00:00
|
|
|
disk6.disk_byte = b;
|
2015-01-10 17:53:38 +00:00
|
|
|
//return b;
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
// ----------------------------------------------------------------------------
|
|
|
|
|
2015-10-16 04:23:21 +00:00
|
|
|
void disk6_init(void) {
|
2013-06-11 07:08:15 +00:00
|
|
|
|
2015-10-16 04:23:21 +00:00
|
|
|
// load Disk II ROM
|
2013-06-11 07:08:15 +00:00
|
|
|
memcpy(apple_ii_64k[0] + 0xC600, slot6_rom, 0x100);
|
|
|
|
|
2013-11-06 05:58:24 +00:00
|
|
|
// disk softswitches
|
|
|
|
// 0xC0Xi : X = slot 0x6 + 0x8 == 0xE
|
2014-12-28 21:07:13 +00:00
|
|
|
cpu65_vmem_r[0xC0E0] = cpu65_vmem_r[0xC0E2] = cpu65_vmem_r[0xC0E4] = cpu65_vmem_r[0xC0E6] = disk_read_phase;
|
2014-11-02 03:02:44 +00:00
|
|
|
cpu65_vmem_r[0xC0E1] = cpu65_vmem_r[0xC0E3] = cpu65_vmem_r[0xC0E5] = cpu65_vmem_r[0xC0E7] = disk_read_phase;
|
|
|
|
|
|
|
|
cpu65_vmem_r[0xC0E8] = disk_read_motor_off;
|
|
|
|
cpu65_vmem_r[0xC0E9] = disk_read_motor_on;
|
|
|
|
cpu65_vmem_r[0xC0EA] = disk_read_select_a;
|
|
|
|
cpu65_vmem_r[0xC0EB] = disk_read_select_b;
|
2014-12-28 21:07:13 +00:00
|
|
|
cpu65_vmem_r[0xC0EC] = disk_read_write_byte;
|
2014-11-02 03:02:44 +00:00
|
|
|
cpu65_vmem_r[0xC0ED] = disk_read_latch;
|
|
|
|
cpu65_vmem_r[0xC0EE] = disk_read_prepare_in;
|
|
|
|
cpu65_vmem_r[0xC0EF] = disk_read_prepare_out;
|
|
|
|
|
2015-10-13 04:48:30 +00:00
|
|
|
for (unsigned int i = 0xC0E0; i < 0xC0F0; i++) {
|
2014-11-02 03:02:44 +00:00
|
|
|
cpu65_vmem_w[i] = cpu65_vmem_r[i];
|
2013-06-11 07:08:15 +00:00
|
|
|
}
|
|
|
|
|
2014-11-02 03:02:44 +00:00
|
|
|
cpu65_vmem_w[0xC0ED] = disk_write_latch;
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
disk6.disk[0].phase = disk6.disk[1].phase = 0;
|
|
|
|
disk6.disk[0].track_valid = disk6.disk[1].track_valid = 0;
|
2015-01-11 20:03:07 +00:00
|
|
|
disk6.motor_time = (struct timespec){ 0 };
|
|
|
|
disk6.motor_off = 1;
|
|
|
|
disk6.drive = 0;
|
2014-12-28 21:07:13 +00:00
|
|
|
disk6.ddrw = 0;
|
|
|
|
}
|
|
|
|
|
2015-10-16 04:23:21 +00:00
|
|
|
const char *disk6_eject(int drive) {
|
2014-12-28 21:07:13 +00:00
|
|
|
|
|
|
|
const char *err = NULL;
|
|
|
|
|
|
|
|
if (disk6.disk[drive].fp) {
|
2015-10-17 20:39:50 +00:00
|
|
|
disk6_flush(drive);
|
2015-10-17 19:02:21 +00:00
|
|
|
TEMP_FAILURE_RETRY(fclose(disk6.disk[drive].fp));
|
|
|
|
|
2015-10-17 04:47:56 +00:00
|
|
|
// foo.dsk -> foo.dsk.gz
|
|
|
|
err = zlib_deflate(disk6.disk[drive].file_name, is_nib(disk6.disk[drive].file_name) ? NIB_SIZE : DSK_SIZE);
|
|
|
|
if (err) {
|
|
|
|
ERRLOG("OOPS: An error occurred when attempting to compress a disk image : %s", err);
|
|
|
|
} else {
|
|
|
|
unlink(disk6.disk[drive].file_name);
|
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
FREE(disk6.disk[drive].file_name);
|
|
|
|
memset(&disk6.disk[drive], 0x0, sizeof(disk6.disk[drive]));
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
return err;
|
|
|
|
}
|
|
|
|
|
2015-10-16 04:23:21 +00:00
|
|
|
const char *disk6_insert(int drive, const char * const raw_file_name, int readonly) {
|
2014-12-28 21:07:13 +00:00
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
disk6_eject(drive);
|
2014-12-28 22:48:53 +00:00
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
disk6.disk[drive].file_name = strdup(raw_file_name);
|
|
|
|
stepper_phases = 0;
|
2015-10-17 20:39:50 +00:00
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
int expected = NIB_SIZE;
|
|
|
|
disk6.disk[drive].nibblized = true;
|
|
|
|
if (!is_nib(disk6.disk[drive].file_name)) {
|
|
|
|
expected = DSK_SIZE;
|
|
|
|
disk6.disk[drive].nibblized = false;
|
|
|
|
disk6.disk[drive].skew_table = skew_table_6_do;
|
|
|
|
if (is_po(disk6.disk[drive].file_name)) {
|
|
|
|
disk6.disk[drive].skew_table = skew_table_6_po;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
}
|
2015-10-17 20:39:50 +00:00
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
char *err = NULL;
|
|
|
|
do {
|
|
|
|
if (is_gz(disk6.disk[drive].file_name)) {
|
|
|
|
err = (char *)zlib_inflate(disk6.disk[drive].file_name, expected); // foo.dsk.gz -> foo.dsk
|
|
|
|
if (err) {
|
|
|
|
ERRLOG("OOPS: An error occurred when attempting to inflate/load a disk image : %s", err);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (unlink(disk6.disk[drive].file_name)) { // temporarily remove .gz file
|
|
|
|
ERRLOG("OOPS, cannot unlink %s", disk6.disk[drive].file_name);
|
|
|
|
}
|
|
|
|
cut_gz(disk6.disk[drive].file_name);
|
|
|
|
}
|
2014-12-28 21:07:13 +00:00
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
struct stat stat_buf;
|
|
|
|
if (stat(disk6.disk[drive].file_name, &stat_buf) < 0) {
|
|
|
|
ERRLOG("OOPS, could not stat %s", disk6.disk[drive].file_name);
|
|
|
|
err = ERR_STAT_FAILED;
|
|
|
|
break;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
if (stat_buf.st_size != expected) {
|
|
|
|
ERRLOG("OOPS, disk image %s does not have expected byte count!", disk6.disk[drive].file_name);
|
|
|
|
err = ERR_IMAGE_NOT_EXPECTED_SIZE;
|
|
|
|
break;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
// open image file
|
|
|
|
TEMP_FAILURE_RETRY_FOPEN(disk6.disk[drive].fp = fopen(disk6.disk[drive].file_name, readonly ? "r" : "r+"));
|
|
|
|
if (!disk6.disk[drive].fp && !readonly) {
|
|
|
|
ERRLOG("OOPS, could not open %s read/write, will attempt to open readonly ...", disk6.disk[drive].file_name);
|
|
|
|
readonly = true;
|
|
|
|
TEMP_FAILURE_RETRY_FOPEN(disk6.disk[drive].fp = fopen(disk6.disk[drive].file_name, "r"));
|
|
|
|
}
|
|
|
|
disk6.disk[drive].is_protected = readonly;
|
|
|
|
if (!disk6.disk[drive].fp) {
|
|
|
|
ERRLOG("OOPS, could not open %s", disk6.disk[drive].file_name);
|
|
|
|
err = ERR_CANNOT_OPEN;
|
|
|
|
break;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2015-10-17 20:39:50 +00:00
|
|
|
disk6.disk[drive].phase = 0;
|
2015-10-17 05:49:59 +00:00
|
|
|
} while (0);
|
2014-12-28 21:07:13 +00:00
|
|
|
|
2015-10-17 05:49:59 +00:00
|
|
|
if (err) {
|
|
|
|
disk6_eject(drive);
|
|
|
|
}
|
|
|
|
|
|
|
|
return err;
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 04:23:21 +00:00
|
|
|
void disk6_flush(int drive) {
|
2015-10-17 20:39:50 +00:00
|
|
|
if (disk6.disk[drive].track_dirty) {
|
|
|
|
ERRLOG("FLUSING WHEN DIRTY?!");
|
|
|
|
}
|
|
|
|
|
|
|
|
TEMP_FAILURE_RETRY(fflush(disk6.disk[drive].fp));
|
2015-10-16 04:23:21 +00:00
|
|
|
}
|
|
|
|
|
2014-12-28 21:07:13 +00:00
|
|
|
#if DISK_TRACING
|
|
|
|
void c_begin_disk_trace_6(const char *read_file, const char *write_file) {
|
|
|
|
if (read_file) {
|
2015-10-17 17:58:51 +00:00
|
|
|
TEMP_FAILURE_RETRY_FOPEN(test_read_fp = fopen(read_file, "w"));
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
if (write_file) {
|
2015-10-17 17:58:51 +00:00
|
|
|
TEMP_FAILURE_RETRY_FOPEN(test_write_fp = fopen(write_file, "w"));
|
2014-12-28 21:07:13 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void c_end_disk_trace_6(void) {
|
|
|
|
if (test_read_fp) {
|
2015-10-16 04:23:21 +00:00
|
|
|
TEMP_FAILURE_RETRY(fflush(test_read_fp));
|
|
|
|
TEMP_FAILURE_RETRY(fclose(test_read_fp));
|
2014-12-28 21:07:13 +00:00
|
|
|
test_read_fp = NULL;
|
|
|
|
}
|
|
|
|
if (test_write_fp) {
|
2015-10-16 04:23:21 +00:00
|
|
|
TEMP_FAILURE_RETRY(fflush(test_write_fp));
|
|
|
|
TEMP_FAILURE_RETRY(fclose(test_write_fp));
|
2014-12-28 21:07:13 +00:00
|
|
|
test_write_fp = NULL;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void c_toggle_disk_trace_6(const char *read_file, const char *write_file) {
|
|
|
|
if (test_read_fp) {
|
|
|
|
c_end_disk_trace_6();
|
|
|
|
} else {
|
|
|
|
c_begin_disk_trace_6(read_file, write_file);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|