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