1 // license:BSD-3-Clause
2 // copyright-holders:Miodrag Milanovic
3 /*********************************************************************
4 
5     formats/cqm_dsk.c
6 
7     CopyQM disk images
8 
9 *********************************************************************/
10 
11 #include <cstring>
12 #include <cassert>
13 #include "flopimg.h"
14 
15 #define CQM_HEADER_SIZE 133
16 
17 struct cqmdsk_tag
18 {
19 	int heads;
20 	int tracks;
21 	int sector_size;
22 	int sector_per_track;
23 	int sector_base;
24 	int interleave;
25 	int skew;
26 
27 	uint8_t* buf;
28 	uint64_t track_offsets[84*2]; /* offset within data for each track */
29 };
30 
31 
get_tag(floppy_image_legacy * floppy)32 static struct cqmdsk_tag *get_tag(floppy_image_legacy *floppy)
33 {
34 	struct cqmdsk_tag *tag;
35 	tag = (cqmdsk_tag *)floppy_tag(floppy );
36 	return tag;
37 }
38 
39 
40 
FLOPPY_IDENTIFY(cqm_dsk_identify)41 FLOPPY_IDENTIFY( cqm_dsk_identify )
42 {
43 	uint8_t header[2];
44 
45 	floppy_image_read(floppy, header, 0, 2);
46 	if (header[0]=='C' && header[1]=='Q') {
47 		*vote = 100;
48 	} else {
49 		*vote = 0;
50 	}
51 	return FLOPPY_ERROR_SUCCESS;
52 }
53 
cqm_get_heads_per_disk(floppy_image_legacy * floppy)54 static int cqm_get_heads_per_disk(floppy_image_legacy *floppy)
55 {
56 	return get_tag(floppy)->heads;
57 }
58 
cqm_get_tracks_per_disk(floppy_image_legacy * floppy)59 static int cqm_get_tracks_per_disk(floppy_image_legacy *floppy)
60 {
61 	return get_tag(floppy)->tracks;
62 }
63 
cqm_get_track_offset(floppy_image_legacy * floppy,int head,int track)64 static uint64_t cqm_get_track_offset(floppy_image_legacy *floppy, int head, int track)
65 {
66 	return get_tag(floppy)->track_offsets[(track<<1) + head];
67 }
68 
get_offset(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,uint64_t * offset)69 static floperr_t get_offset(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, uint64_t *offset)
70 {
71 	uint64_t pos = 0;
72 	uint8_t data;
73 	int16_t len;
74 	int s;
75 
76 	if ((head < 0) || (head >= get_tag(floppy)->heads) || (track < 0) || (track >= get_tag(floppy)->tracks)
77 			|| (sector < 0) )
78 		return FLOPPY_ERROR_SEEKERROR;
79 
80 	pos = cqm_get_track_offset(floppy,head,track);
81 	s = 0;
82 	do {
83 		floppy_image_read(floppy, &len, pos, 2);
84 		pos+=2;
85 		if(len<0) {
86 			floppy_image_read(floppy, &data, pos, 1);
87 			memset(get_tag(floppy)->buf + s,data, -len);
88 			pos++;
89 			s += -len;
90 		} else {
91 			floppy_image_read(floppy, get_tag(floppy)->buf + s, pos, len);
92 			pos+=len;
93 			s += len;
94 		}
95 	} while(s<get_tag(floppy)->sector_size*get_tag(floppy)->sector_per_track);
96 
97 	if (offset)
98 		*offset = sector * get_tag(floppy)->sector_size;
99 	return FLOPPY_ERROR_SUCCESS;
100 }
101 
102 
103 
internal_cqm_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,bool sector_is_index,void * buffer,size_t buflen)104 static floperr_t internal_cqm_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, bool sector_is_index, void *buffer, size_t buflen)
105 {
106 	uint64_t offset;
107 	floperr_t err;
108 
109 	// take sector offset
110 	err = get_offset(floppy, head, track, sector, sector_is_index, &offset);
111 	if (err)
112 		return err;
113 	memcpy(buffer,get_tag(floppy)->buf+offset,buflen);
114 	return FLOPPY_ERROR_SUCCESS;
115 }
116 
117 
cqm_read_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)118 static floperr_t cqm_read_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
119 {
120 	return internal_cqm_read_sector(floppy, head, track, sector, false, buffer, buflen);
121 }
122 
cqm_read_indexed_sector(floppy_image_legacy * floppy,int head,int track,int sector,void * buffer,size_t buflen)123 static floperr_t cqm_read_indexed_sector(floppy_image_legacy *floppy, int head, int track, int sector, void *buffer, size_t buflen)
124 {
125 	return internal_cqm_read_sector(floppy, head, track, sector, true, buffer, buflen);
126 }
127 
cqm_get_sector_length(floppy_image_legacy * floppy,int head,int track,int sector,uint32_t * sector_length)128 static floperr_t cqm_get_sector_length(floppy_image_legacy *floppy, int head, int track, int sector, uint32_t *sector_length)
129 {
130 	floperr_t err;
131 	err = get_offset(floppy, head, track, sector, false, nullptr);
132 	if (err)
133 		return err;
134 
135 	if (sector_length) {
136 		*sector_length = get_tag(floppy)->sector_size;
137 	}
138 	return FLOPPY_ERROR_SUCCESS;
139 }
140 
cqm_get_indexed_sector_info(floppy_image_legacy * floppy,int head,int track,int sector_index,int * cylinder,int * side,int * sector,uint32_t * sector_length,unsigned long * flags)141 static floperr_t cqm_get_indexed_sector_info(floppy_image_legacy *floppy, int head, int track, int sector_index, int *cylinder, int *side, int *sector, uint32_t *sector_length, unsigned long *flags)
142 {
143 	if (sector_index >= get_tag(floppy)->sector_per_track) return FLOPPY_ERROR_SEEKERROR;
144 
145 	if (cylinder) {
146 		*cylinder = track;
147 	}
148 	if (side) {
149 		*side = head;
150 	}
151 	if (sector) {
152 		*sector = get_tag(floppy)->sector_base + sector_index;
153 	}
154 	if (sector_length) {
155 		*sector_length = get_tag(floppy)->sector_size;
156 	}
157 	if (flags)
158 		*flags = 0;
159 	return FLOPPY_ERROR_SUCCESS;
160 }
161 
FLOPPY_CONSTRUCT(cqm_dsk_construct)162 FLOPPY_CONSTRUCT( cqm_dsk_construct )
163 {
164 	struct FloppyCallbacks *callbacks;
165 	struct cqmdsk_tag *tag;
166 	uint8_t header[CQM_HEADER_SIZE];
167 	uint64_t pos = 0;
168 	int16_t len;
169 	int head;
170 	int track;
171 	int s;
172 	if(params)
173 	{
174 		// create
175 		return FLOPPY_ERROR_UNSUPPORTED;
176 	}
177 
178 	tag = (struct cqmdsk_tag *) floppy_create_tag(floppy, sizeof(struct cqmdsk_tag));
179 	if (!tag)
180 		return FLOPPY_ERROR_OUTOFMEMORY;
181 
182 	floppy_image_read(floppy, header, 0, CQM_HEADER_SIZE);
183 
184 	tag->sector_size      = (header[0x04] << 8) + header[0x03];
185 	tag->sector_per_track = header[0x10];
186 	tag->heads            = header[0x12];
187 	tag->tracks           = header[0x5b];
188 	tag->sector_base      = header[0x71] + 1;
189 	tag->interleave       = header[0x74];
190 	tag->skew             = header[0x75];
191 
192 	// header + comment size to position on first data block
193 
194 	pos = CQM_HEADER_SIZE + (header[0x70] << 8) + header[0x6f];
195 	track = 0;
196 	head = 0;
197 	tag->buf = (uint8_t*)malloc(tag->sector_size*tag->sector_per_track);
198 	do {
199 		tag->track_offsets[(track<<1) + head] = pos;
200 		s = 0;
201 		do {
202 			floppy_image_read(floppy, &len, pos, 2);
203 			pos+=2;
204 			if(len<0) {
205 				pos++;
206 				s += -len;
207 			} else {
208 				pos+=len;
209 				s += len;
210 			}
211 		} while(s<tag->sector_size*tag->sector_per_track);
212 		if(head ==0 && tag->heads > 1) {
213 			head = 1;
214 		} else {
215 			head = 0;
216 			track++;
217 		}
218 	} while(pos < floppy_image_size(floppy));
219 
220 
221 	callbacks = floppy_callbacks(floppy);
222 	callbacks->read_sector = cqm_read_sector;
223 	callbacks->read_indexed_sector = cqm_read_indexed_sector;
224 	callbacks->get_sector_length = cqm_get_sector_length;
225 	callbacks->get_heads_per_disk = cqm_get_heads_per_disk;
226 	callbacks->get_tracks_per_disk = cqm_get_tracks_per_disk;
227 	callbacks->get_indexed_sector_info = cqm_get_indexed_sector_info;
228 
229 	return FLOPPY_ERROR_SUCCESS;
230 }
231 
232 
233 
234 
235 /*********************************************************************
236 
237     formats/cqm_dsk.c
238 
239     CopyQM disk images
240 
241 *********************************************************************/
242 
243 #include "cqm_dsk.h"
244 
cqm_format()245 cqm_format::cqm_format()
246 {
247 }
248 
name() const249 const char *cqm_format::name() const
250 {
251 	return "cqm";
252 }
253 
description() const254 const char *cqm_format::description() const
255 {
256 	return "CopyQM disk image";
257 }
258 
extensions() const259 const char *cqm_format::extensions() const
260 {
261 	return "cqm,cqi,dsk";
262 }
263 
identify(io_generic * io,uint32_t form_factor)264 int cqm_format::identify(io_generic *io, uint32_t form_factor)
265 {
266 	uint8_t h[3];
267 	io_generic_read(io, h, 0, 3);
268 
269 	if (h[0] == 'C' && h[1] == 'Q' && h[2] == 0x14)
270 		return 100;
271 
272 	return 0;
273 }
274 
load(io_generic * io,uint32_t form_factor,floppy_image * image)275 bool cqm_format::load(io_generic *io, uint32_t form_factor, floppy_image *image)
276 {
277 	const int max_size = 4*1024*1024; // 4MB ought to be large enough for any floppy
278 	std::vector<uint8_t> imagebuf(max_size);
279 	uint8_t header[CQM_HEADER_SIZE];
280 	io_generic_read(io, header, 0, CQM_HEADER_SIZE);
281 
282 	int sector_size      = (header[0x04] << 8) | header[0x03];
283 	int sector_per_track = (header[0x11] << 8) | header[0x10];
284 	int heads            = (header[0x13] << 8) | header[0x12];
285 	int tracks           = header[0x5b];
286 //  int blind            = header[0x58];    // 0=DOS, 1=blind, 2=HFS
287 	int density          = header[0x59];    // 0=DD, 1=HD, 2=ED
288 	int comment_size     = (header[0x70] << 8) | header[0x6f];
289 	int sector_base      = header[0x71] + 1;
290 //  int interleave       = header[0x74];    // TODO
291 //  int skew             = header[0x75];    // TODO
292 //  int drive            = header[0x76];    // source drive type: 1=5.25" 360KB, 2=5.25" 1.2MB, 3=3.5" 720KB, 4=3.5" 1.44MB, 6=3.5" 2.88MB, 8" is unknown (0 or 5?)
293 
294 	switch(density)
295 	{
296 		case 0:
297 			if (form_factor == floppy_image::FF_525 && tracks > 50)
298 				image->set_variant(heads == 1 ? floppy_image::SSQD : floppy_image::DSQD);
299 			else
300 				image->set_variant(heads == 1 ? floppy_image::SSDD : floppy_image::DSDD);
301 			break;
302 		case 1:
303 			if (heads == 1)
304 				return false; // single side HD ?
305 			image->set_variant(floppy_image::DSHD);
306 			break;
307 		case 2:
308 			if (heads == 1)
309 				return false; // single side ED ?
310 			image->set_variant(floppy_image::DSED);
311 		default:
312 			return false;
313 	}
314 
315 	static const int rates[3] = { 250000, 300000, 500000 };
316 	int rate = density >= 3 ? 500000 : rates[density];
317 	int rpm = form_factor == floppy_image::FF_8 || (form_factor == floppy_image::FF_525 && rate >= 300000) ? 360 : 300;
318 	int base_cell_count = rate*60/rpm;
319 
320 	int cqm_size = io_generic_size(io);
321 	std::vector<uint8_t> cqmbuf(cqm_size);
322 	io_generic_read(io, &cqmbuf[0], 0, cqm_size);
323 
324 	// decode the RLE data
325 	for (int s = 0, pos = CQM_HEADER_SIZE + comment_size; pos < cqm_size; )
326 	{
327 		int16_t len = (cqmbuf[pos + 1] << 8) | cqmbuf[pos];
328 		pos += 2;
329 		if(len < 0)
330 		{
331 			len = -len;
332 			memset(&imagebuf[s], cqmbuf[pos], len);
333 			pos++;
334 		}
335 		else
336 		{
337 			memcpy(&imagebuf[s], &cqmbuf[pos], len);
338 			pos += len;
339 		}
340 
341 		s += len;
342 	}
343 
344 	int ssize;
345 	for(ssize=0; (128 << ssize) < sector_size; ssize++)
346 		;
347 
348 	desc_pc_sector sects[256];
349 	for(int track = 0, pos = 0; track < tracks; track++)
350 		for(int head = 0; head < heads; head++)
351 		{
352 			for(int sector = 0; sector < sector_per_track; sector++)
353 			{
354 				sects[sector].track       = track;
355 				sects[sector].head        = head;
356 				sects[sector].sector      = sector_base + sector;
357 				sects[sector].size        = ssize;
358 				sects[sector].deleted     = false;
359 				sects[sector].bad_crc     = false;
360 				sects[sector].actual_size = sector_size;
361 				sects[sector].data        = &imagebuf[pos];
362 				pos += sector_size;
363 			}
364 
365 			build_pc_track_mfm(track, head, image, base_cell_count*2, sector_per_track, sects, calc_default_pc_gap3_size(form_factor, sector_size));
366 		}
367 
368 	return true;
369 }
370 
save(io_generic * io,floppy_image * image)371 bool cqm_format::save(io_generic *io, floppy_image *image)
372 {
373 	return false;
374 }
375 
supports_save() const376 bool cqm_format::supports_save() const
377 {
378 	return false;
379 }
380 
381 const floppy_format_type FLOPPY_CQM_FORMAT = &floppy_image_format_creator<cqm_format>;
382