OXYGENHUB
◆ main openmsx / Contrib / dmk / svicpm2dmk.cc
5.0 KiB · C++ · last changed chore(): initial commit 1 month ago source blame history raw
1
#include "dmk-common.hh"
2
3
#include <array>
4
#include <cassert>
5
#include <cstdint>
6
#include <cstdio>
7
#include <cstdlib>
8
#include <cstring>
9
#include <span>
10
#include <stdexcept>
11
#include <string>
12
#include <sys/stat.h>
13
#include <sys/types.h>
14
#include <vector>
15
16
17
static constexpr unsigned TRACK_LENGTH = 6250;
18
19
struct DiskInfo
20
{
21
	int gap1;
22
	int gap2;
23
	int gap3;
24
	int gap4a;
25
	int sectorsPerTrack;
26
	int numberCylinders;
27
	int sectorSizeCode;
28
};
29
30
static void fill(uint8_t*& p, int len, uint8_t value)
31
{
32
	memset(p, value, len);
33
	p += len;
34
}
35
36
void convert(const DiskInfo& info, const std::string& input, const std::string& output)
37
{
38
	// Single or double sided input image?
39
	struct stat st;
40
	stat(input.c_str(), &st);
41
	int numSides = [&]{
42
		switch (st.st_size) {
43
			case 174080:
44
				return 1;
45
			case 348160:
46
				return 2;
47
			default:
48
				throw std::runtime_error(
49
					"input filesize should be exactly 174080 or 348160 bytes.\n");
50
		}
51
	}();
52
53
	int sectorSize = 128 << info.sectorSizeCode;
54
	int totalTracks = numSides * info.numberCylinders;
55
	int totalSectors = totalTracks * info.sectorsPerTrack;
56
	int totalSize = totalSectors * sectorSize;
57
58
	// sanity check
59
	if (st.st_size != totalSize) {
60
		throw std::runtime_error("Wrong input filesize");
61
	}
62
63
	File inf(input, "rb");
64
	File outf(output, "wb");
65
66
	int rawSectorLen =
67
		12 + 10 + info.gap2 + 12 + 4 +
68
		sectorSize + 2 + info.gap3;
69
70
	int gap4b = TRACK_LENGTH - (info.gap4a + 12 + 4 + info.gap1 +
71
		info.sectorsPerTrack * rawSectorLen);
72
	assert(gap4b > 0);
73
	int dmkTrackLen = TRACK_LENGTH + 128;
74
75
	DmkHeader header;
76
	memset(&header, 0, sizeof(header));
77
	header.numTracks = info.numberCylinders;
78
	header.trackLen[0] = dmkTrackLen & 0xff;
79
	header.trackLen[1] = dmkTrackLen >> 8;
80
	header.flags = ((numSides == 2) ? (0 << 4) : (1 << 4)) |
81
	               (0 << 6); // double density (MFM)
82
	outf.write(&header, sizeof(header));
83
84
	std::vector<uint8_t*> addrPos(info.sectorsPerTrack);
85
	std::vector<uint8_t*> dataPos(info.sectorsPerTrack);
86
	std::vector<uint8_t> buf(dmkTrackLen); // zero-initialized
87
	uint8_t* ip = buf.data() +   0; // pointer in IDAM table
88
	uint8_t* tp = buf.data() + 128; // pointer in actual track data
89
90
	fill(tp, info.gap4a, 0x4e); // gap4a
91
	fill(tp,         12, 0x00); // sync
92
	fill(tp,          3, 0xc2); // index mark
93
	fill(tp,          1, 0xfc); //
94
	fill(tp, info.gap1,  0x4e); // gap1
95
	for (int sec = 0; sec < info.sectorsPerTrack; ++sec) {
96
		fill(tp,         12, 0x00); // sync
97
		fill(tp,          3, 0xa1); // ID addr mark
98
		int pos = tp - buf.data();
99
		assert(pos < 0x4000);
100
		*ip++ = pos & 0xff;
101
		*ip++ = (pos >> 8) | 0x80; // double density (MFM) sector
102
		fill(tp,          1, 0xfe); // ID addr mark (cont)
103
		addrPos[sec] = tp;
104
		fill(tp,          6, 0x00); // C H R N CRC (overwritten later)
105
		fill(tp, info.gap2,  0x4e); // gap2
106
		fill(tp,         12, 0x00); // sync
107
		fill(tp,          3, 0xa1); // data mark
108
		fill(tp,          1, 0xfb); //
109
		dataPos[sec] = tp;
110
		fill(tp, sectorSize, 0x00); // sector data (overwritten later)
111
		fill(tp,          2, 0x00); // CRC (overwritten later)
112
		fill(tp, info.gap3,  0x4e); // gap3
113
	}
114
	fill(tp, gap4b, 0x4e); // gap4b
115
	assert((tp - buf.data()) == dmkTrackLen);
116
117
	for (int cyl = 0; cyl < info.numberCylinders; ++cyl) {
118
		for (int head = 0; head < numSides; ++head) {
119
			for (int sec = 0; sec < info.sectorsPerTrack; ++sec) {
120
				uint8_t* ap = addrPos[sec];
121
				*ap++ = cyl;
122
				*ap++ = head;
123
				*ap++ = sec + 1;
124
				*ap++ = info.sectorSizeCode;
125
126
				uint16_t addrCrc = 0xffff;
127
				assert(ap >= &buf[8]);
128
				const uint8_t* t1 = ap - 8;
129
				for (int i = 0; i < 8; ++i) {
130
					updateCrc(addrCrc, t1[i]);
131
				}
132
				*ap++ = addrCrc >> 8;
133
				*ap++ = addrCrc & 0xff;
134
135
				uint8_t* dp = dataPos[sec];
136
				inf.read(dp, sectorSize);
137
				dp += sectorSize;
138
139
				uint16_t dataCrc = 0xffff;
140
				const uint8_t* t2 = dp - sectorSize - 4;
141
				for (int i = 0; i < sectorSize + 4; ++i) {
142
					updateCrc(dataCrc, t2[i]);
143
				}
144
				*dp++ = dataCrc >> 8;
145
				*dp++ = dataCrc & 0xff;
146
			}
147
			outf.write(buf.data(), dmkTrackLen);
148
		}
149
	}
150
}
151
152
int main(int argc, const char** argv)
153
{
154
	std::span arg(argv, argc);
155
	if (arg.size() != 3) {
156
		printf("svicpm2dmk\n"
157
			"---------\n"
158
			"\n"
159
			"Utility to convert a SVI CP/M disk image into a DMK disk image.\n"
160
			"\n"
161
			"The SVI CP/M image is expected to have to following format:\n"
162
			" - All tracks contain 17 sectors of 256 bytes.\n"
163
			" - There are 40 tracks on the disk.\n"
164
			" - The disk can be either single or double sided.\n"
165
			"This means that the size of the input image should be exactly\n"
166
			"174080 bytes for single sided disks or 348160 bytes for double\n"
167
			"sided disks.\n"
168
			"\n"
169
			"Usage: %s <input.dsk> <output.dmk>\n", arg[0]);
170
		exit(1);
171
	}
172
173
	// TODO add command line options to make these parameters configurable
174
	DiskInfo info;
175
	info.gap1  =  50;
176
	info.gap2  =  22;
177
	info.gap3  =  34;
178
	info.gap4a =  80;
179
	info.sectorsPerTrack = 17;
180
	info.numberCylinders = 40;
181
	info.sectorSizeCode = 1; // 256 = 128 << 1
182
183
	try {
184
		convert(info, arg[1], arg[2]);
185
	} catch (std::exception& e) {
186
		fprintf(stderr, "Error: %s\n", e.what());
187
	}
188
}