OXYGENHUB
◆ main openmsx / Contrib / dmk / analyze-dmk.cc
6.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
e8555c89 Andy McCall 1
#include "dmk-common.hh"
2
3
#include <cassert>
4
#include <cstdlib>
5
#include <span>
6
#include <string>
7
#include <vector>
8
9
10
[[nodiscard]] static int modulo(int x, int y) {
11
	int z = x % y; // this calculates the remainder, not modulo!
12
	if (z < 0) z += y;
13
	return z;
14
}
15
16
struct DmkTrackData {
17
public:
18
	void read(const File& file, int dmkTrackLen) {
19
		assert(dmkTrackLen > 128);
20
		buffer.resize(dmkTrackLen);
21
		file.read(buffer.data(), dmkTrackLen);
22
	}
23
	[[nodiscard]] int readDmkIndex(unsigned i) const {
24
		assert(i < 64);
25
		return buffer[2 * i + 0] + 256 * buffer[2 * i + 1];
26
27
	}
28
	[[nodiscard]] uint8_t readCircular(int idx) const
29
	{
30
		return buffer[128 + trackIdx(idx)];
31
	}
32
	[[nodiscard]] int trackIdx(int idx) const {
33
		return modulo(idx, trackLength());
34
	}
35
	[[nodiscard]] int trackLength() const {
36
		assert(buffer.size() > 128);
37
		return int(buffer.size() - 128);
38
	}
39
40
private:
41
	std::vector<uint8_t> buffer;
42
};
43
44
45
46
static bool isValidDmkHeader(const DmkHeader& header)
47
{
48
	if ((header.writeProtected != 0x00) &&
49
	    (header.writeProtected != 0xff)) {
50
		return false;
51
	}
52
	int trackLen = header.trackLen[0] + 256 * header.trackLen[1];
53
	if (trackLen >= 0x4000) return false; // too large track length
54
	if (trackLen <= 128)    return false; // too small
55
	if (header.flags & ~0xd0) return false; // unknown flag set
56
	for (int i = 0; i < 7 + 4; ++i) {
57
		if (header.reserved[i] != 0) return false;
58
	}
59
	return true;
60
}
61
62
63
static void analyzeTrack(const DmkTrackData& track)
64
{
65
	for (int i = 0; i < 64; ++i) {
66
		// Get (and check) pointer into track data
67
		int dmkIdx = track.readDmkIndex(i);
68
		if (dmkIdx == 0) {
69
			// end of table reached
70
			break;
71
		}
72
		printf("%2d: ", i);
73
		if ((dmkIdx & 0xC000) != 0x8000) {
74
			printf("... skipping single-density sector\n");
75
			continue;
76
		}
77
		dmkIdx &= ~0xC000; // clear flags
78
		if ((dmkIdx < 128) || ((dmkIdx + 128) >= track.trackLength())) {
79
			printf("... skipping invalid IDAM offset (wrong DMK file)\n");
80
			continue;
81
		}
82
		dmkIdx -= 128;
83
84
		// read (and check) address mark
85
		int addrIdx = track.trackIdx(dmkIdx - 3);
86
		uint8_t d0 = track.readCircular(addrIdx + 0);
87
		uint8_t d1 = track.readCircular(addrIdx + 1);
88
		uint8_t d2 = track.readCircular(addrIdx + 2);
89
		uint8_t d3 = track.readCircular(addrIdx + 3);
90
		uint8_t c  = track.readCircular(addrIdx + 4);
91
		uint8_t h  = track.readCircular(addrIdx + 5);
92
		uint8_t r  = track.readCircular(addrIdx + 6);
93
		uint8_t n  = track.readCircular(addrIdx + 7);
94
		uint8_t ch = track.readCircular(addrIdx + 8);
95
		uint8_t cl = track.readCircular(addrIdx + 9);
96
97
		if ((d0 != 0xA1) || (d1 != 0xA1) || (d2 != 0xA1) || (d3 != 0xFE)) {
98
			printf("... skipping wrong IDAM entry, does not point to an address mark\n");
99
			continue;
100
		}
101
102
		// address mark CRC
103
		uint16_t addrCrc = 0xFFFF;
104
		updateCrc(addrCrc, d0);
105
		updateCrc(addrCrc, d1);
106
		updateCrc(addrCrc, d2);
107
		updateCrc(addrCrc, d3);
108
		updateCrc(addrCrc, c);
109
		updateCrc(addrCrc, h);
110
		updateCrc(addrCrc, r);
111
		updateCrc(addrCrc, n);
112
		int onDiskAddrCrc = 256 * ch + cl;
113
		bool addrCrcErr = onDiskAddrCrc != addrCrc;
114
115
		// print address mark info
116
		printf("AOfst=%4d C=%3d H=%3d R=%3d N=%3d ACrc=%04x,%s",
117
		       addrIdx, c, h, r, n, onDiskAddrCrc,
118
		       (addrCrcErr ? "ERR\n" : "ok "));
119
		if (onDiskAddrCrc != addrCrc) {
120
			continue;
121
		}
122
123
		// locate data mark, should be within 43 bytes from end
124
		// of address mark (according to WD2793 datasheet)
125
		int j;
126
		for (j = 10; j < 53; ++j) {
127
			int dataIdx = addrIdx + j;
128
			uint8_t a0 = track.readCircular(dataIdx + 0);
129
			uint8_t a1 = track.readCircular(dataIdx + 1);
130
			uint8_t a2 = track.readCircular(dataIdx + 2);
131
			uint8_t t  = track.readCircular(dataIdx + 3);
132
			if ((a0 != 0xA1) || (a1 != 0xA1) || (a2 != 0xA1)) {
133
				continue;
134
			}
135
136
			// calculate data CRC, data mark part
137
			uint16_t dataCrc = 0xFFFF;
138
			updateCrc(dataCrc, a0);
139
			updateCrc(dataCrc, a1);
140
			updateCrc(dataCrc, a2);
141
			updateCrc(dataCrc, t);
142
143
			// actual sector data
144
			int sectorSize = 128 << (n & 7);
145
			for (int k = 0; k < sectorSize; ++k) {
146
				uint8_t d = track.readCircular(dataIdx + 4 + k);
147
				updateCrc(dataCrc, d);
148
			}
149
150
			uint8_t crc1 = track.readCircular(dataIdx + 4 + sectorSize + 0);
151
			uint8_t crc2 = track.readCircular(dataIdx + 4 + sectorSize + 1);
152
			int onDiskDataCrc = 256 * crc1 + crc2;
153
			bool dataCrcErr = onDiskDataCrc != dataCrc;
154
155
			char type = (t == 0xFB) ? 'n' :
156
			            (t == 0xF8) ? 'd' :
157
			                          '?';
158
			printf(" DOfst=%4d T=%c DCrc=%04x,%s\n",
159
			       dataIdx, type, onDiskDataCrc,
160
			       (dataCrcErr ? "ERR" : "ok "));
161
			break;
162
		}
163
		if (j == 53) {
164
			printf(" data mark not found within 43 bytes from address mark\n");
165
		}
166
	}
167
}
168
169
static void analyzeDisk(const std::string& input)
170
{
171
	File inf(input, "rb");
172
	DmkHeader header;
173
	inf.read(&header, sizeof(header));
174
	if (!isValidDmkHeader(header)) {
175
		throw std::runtime_error("Invalid DMK header");
176
	}
177
178
	int numCylinders = header.numTracks;
179
	int numSides = (header.flags & 0x10) ? 1 : 2;
180
	int dmkTrackLen = header.trackLen[0] + 256 * header.trackLen[1];
181
182
	printf("Legend:\n"
183
	       "  AOfst:  address mark offset in track\n"
184
	       "  C:      cylinder\n"
185
	       "  H:      head\n"
186
	       "  R:      record (sector number)\n"
187
	       "  N:      sector size  (in bytes: 128 << N)\n"
188
	       "  ACrc:   CRC value of the address block\n"
189
	       "  DOfst:  data mark offset in track\n"
190
	       "  T:      data mark type (n = normal, d = deleted, ? = unknown)\n"
191
	       "  DCrc:   CRC value of data block\n"
192
	       "\n");
193
194
	printf("Raw track length = %d bytes\n\n", dmkTrackLen - 128);
195
196
	DmkTrackData track;
197
	for (int t = 0; t < numCylinders; ++t) {
198
		for (int h = 0; h < numSides; ++h) {
199
			printf("-- physical track %d, head %d\n", t, h);
200
			track.read(inf, dmkTrackLen);
201
			analyzeTrack(track);
202
		}
203
	}
204
}
205
206
int main(int argc, const char** argv)
207
{
208
	std::span arg(argv, argc);
209
	if (arg.size() != 2) {
210
		printf("analyze-dmk\n"
211
		       "\n"
212
		       "Analyze the content of a DMK disk image.\n"
213
		       "\n"
214
		       "usage: %s <file.dmk>\n", arg[0]);
215
		exit(1);
216
	}
217
	try {
218
		analyzeDisk(arg[1]);
219
	} catch (std::exception& e) {
220
		fprintf(stderr, "Error: %s\n", e.what());
221
	}
222
}