1 /***************************************************************************
2  *                                                                         *
3  *    LIBDSK: General floppy and diskimage access library                  *
4  *    Copyright (C) 2001-2,2015  John Elliott <seasip.webmaster@gmail.com>     *
5  *                                                                         *
6  *    This library is free software; you can redistribute it and/or        *
7  *    modify it under the terms of the GNU Library General Public          *
8  *    License as published by the Free Software Foundation; either         *
9  *    version 2 of the License, or (at your option) any later version.     *
10  *                                                                         *
11  *    This library is distributed in the hope that it will be useful,      *
12  *    but WITHOUT ANY WARRANTY; without even the implied warranty of       *
13  *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU    *
14  *    Library General Public License for more details.                     *
15  *                                                                         *
16  *    You should have received a copy of the GNU Library General Public    *
17  *    License along with this library; if not, write to the Free           *
18  *    Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,      *
19  *    MA 02111-1307, USA                                                   *
20  *                                                                         *
21  ***************************************************************************/
22 
23 
24 /* Declarations for the JV3 driver */
25 
26 /* JV3 spec at <http://www.tim-mann.org/trs80/dskspec.html> */
27 
28 #include "drvi.h"
29 #include "drvjv3.h"
30 
31 
32 DRV_CLASS dc_jv3 =
33 {
34 	sizeof(JV3_DSK_DRIVER),
35 	"jv3",
36 	"JV3 file driver",
37 	jv3_open,
38 	jv3_creat,
39 	jv3_close,
40 	jv3_read,
41 	jv3_write,
42 	jv3_format,
43 	jv3_getgeom,
44 	jv3_secid,
45 	jv3_xseek,
46 	jv3_status,
47 	jv3_xread,
48 	jv3_xwrite,
49 	NULL,			/* jv3_tread */
50 	NULL,			/* jv3_xtread */
51 	NULL,			/* jv3_option_enum */
52 	NULL,			/* jv3_option_set */
53 	NULL,			/* jv3_option_get */
54 	jv3_trackids,
55 	NULL			/* jv3_rtread */
56 };
57 
58 /* #define MONITOR(x) printf x     */
59 
60 #define MONITOR(x)
61 
62 
63 #define DC_CHECK(s) \
64 	JV3_DSK_DRIVER *self; \
65 	if (s->dr_class != &dc_jv3) return DSK_ERR_BADPTR; \
66 	self = (JV3_DSK_DRIVER *)s;
67 
68 typedef struct
69 {
70 	/* Currently-loaded header block */
71 	unsigned char cur_header[JV3_HEADER_LEN];
72 
73 	/* Its location within the disk file */
74 	long header_offset;
75 
76 	/* Did the callback change anything? */
77 	int touched;
78 
79 	/* Stop processing? */
80 	int stop;
81 
82 	/* Offset of the current sector */
83 	long data_offset;
84 
85 	/* Pointer to the encoded header of the current sector */
86 	unsigned char *sector_head;
87 
88 	/* Location of the current sector, and its flags */
89 	dsk_pcyl_t  cyl;
90 	dsk_phead_t head;
91 	dsk_psect_t sector;
92 	unsigned char flags;
93 	/* Size of the current sector */
94 	size_t secsize;
95 
96 	/* Is the current sector an empty slot? */
97 	int isfree;
98 } JV3_ENUM_STATE;
99 
100 
101 /* If the header in 'st' has been touched, write the changes out */
flush_header(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * st)102 dsk_err_t flush_header(JV3_DSK_DRIVER *self, JV3_ENUM_STATE *st)
103 {
104 	long pos;
105 
106 	if (st->touched)
107 	{
108 		/* Get current file position */
109 		pos = ftell(self->jv3_fp);
110 		if (pos < 0) return DSK_ERR_SYSERR;
111 
112 		/* Seek to the header */
113 		if (fseek(self->jv3_fp, st->header_offset, SEEK_SET) < 0)
114 		{
115 			return DSK_ERR_SYSERR;
116 		}
117 		/* Write the header */
118 		if (fwrite(st->cur_header, 1, JV3_HEADER_LEN, self->jv3_fp)
119 							< JV3_HEADER_LEN)
120 		{
121 			return DSK_ERR_SYSERR;
122 		}
123 		/* Reset the 'touched' flag */
124 		st->touched = 0;
125 		/* And seek back to where we were */
126 		if (fseek(self->jv3_fp, pos, SEEK_SET) < 0)
127 		{
128 			return DSK_ERR_SYSERR;
129 		}
130 		/* Update cached header if appropriate */
131 		if (st->header_offset == 0)
132 			memcpy(self->jv3_header, st->cur_header, JV3_HEADER_LEN);
133 	}
134 	return DSK_ERR_OK;
135 }
136 
137 /* Decode the 2-bit size code as a sector size. Annoyingly the encoding
138  * is different for used and free sectors */
decode_size(int isfree,unsigned char size)139 static size_t decode_size(int isfree, unsigned char size)
140 {
141 	switch (size & JV3_SIZE)
142 	{
143 		case 0: return (isfree) ?  512 :  256;
144 		case 1: return (isfree) ? 1024 :  128;
145 		case 2: return (isfree) ?  128 : 1024;
146 		case 3: return (isfree) ?  256 :  512;
147 	}
148 	/* Can't happen */
149 	return 256;
150 }
151 
152 
153 /* Encode the sector size as a 2-bit size code. Annoyingly the encoding
154  * is different for used and free sectors */
encode_size(int isfree,size_t size)155 unsigned char encode_size(int isfree, size_t size)
156 {
157 	switch (size)
158 	{
159 		case  128: return (isfree) ? 2 : 1;
160 		default:
161 		case  256: return (isfree) ? 3 : 0;
162 		case  512: return (isfree) ? 0 : 3;
163 		case 1024: return (isfree) ? 1 : 2;
164 	}
165 }
166 
167 
168 
169 
170 
171 typedef dsk_err_t (*JV3_CALLBACK)(JV3_DSK_DRIVER *self,
172 				JV3_ENUM_STATE *state, void *param);
173 
174 /* Because of the awkward chunked nature of JV3 (it consists of one or more
175  * blocks, each containing 0-JV3_HEADER_COUNT sectors, and you need to parse
176  * each block completely to find the next) this driver uses a callback
177  * mechanism. jv3_enum_sectors() handles all the grubby business of
178  * enumerating each sector in turn
179  *
180  * The function should return DSK_ERR_OK to continue, other values to abort
181  * with error.
182  *
183  * To stop the enumeration set state->stop to nonzero.
184  */
jv3_enum_sectors(JV3_DSK_DRIVER * self,unsigned char append,JV3_CALLBACK cbk,void * param)185 dsk_err_t jv3_enum_sectors(JV3_DSK_DRIVER *self, unsigned char append,
186 		JV3_CALLBACK cbk, void *param)
187 {
188 	JV3_ENUM_STATE state;
189 
190 	dsk_err_t err;
191 	int n;
192 
193 	memset(&state, 0, sizeof(state));
194 
195 	memcpy(state.cur_header, self->jv3_header, JV3_HEADER_LEN);
196 	state.header_offset = 0;
197 	state.data_offset   = JV3_HEADER_LEN;
198 
199 	while (state.data_offset < self->jv3_len)
200 	{
201 		for (n = 0; n < JV3_HEADER_COUNT; n++)
202 		{
203 			state.sector_head = state.cur_header + 3 * n;
204 			state.cyl    = state.sector_head[0];
205 			state.sector = state.sector_head[1];
206 			state.flags  = state.sector_head[2];
207 			state.head   = (state.flags & JV3_SIDE) ? 1 : 0;
208 			state.isfree = (state.cyl == JV3_FREE
209 						&& state.sector == JV3_FREE);
210 			state.secsize = decode_size(state.isfree, state.flags);
211 
212 			err = (*cbk)(self, &state, param);
213 
214 			if (state.stop)
215 			{
216 				dsk_err_t e2 = flush_header(self, &state);
217 				return (err == DSK_ERR_OK) ? e2 : err;
218 			}
219 
220 			state.data_offset += state.secsize;
221 /* If we have reached EOF and 'stop' is not set and 'append' is nonzero,
222  * append a blank sector of the appropriate type */
223 
224 			if (state.data_offset >= self->jv3_len)
225 			{
226 				if (append && !state.stop &&
227 					n < (JV3_HEADER_COUNT - 1))
228 				{
229 /* sector_head points at the last valid sector. If there is space for
230  * another sector in the current header, insert it */
231 					state.sector_head += 3;
232 					state.sector_head[0] = JV3_FREE;
233 					state.sector_head[1] = JV3_FREE;
234 					state.sector_head[2] = append;
235 					state.flags  = append;
236 					state.isfree = 1;
237 					state.secsize = decode_size(1, append);
238 
239 					err = (*cbk)(self, &state, param);
240 					if (err)
241 					{
242 						flush_header(self, &state);
243 						return err;
244 					}
245 					append = 0;
246 				}
247 				break;
248 			}
249 		}
250 		/* Have searched the header to no avail. Try the next
251 		 * header. */
252 		if (fseek(self->jv3_fp, state.data_offset, SEEK_SET) < 0)
253 		{
254 			flush_header(self, &state);
255 			return DSK_ERR_SYSERR;
256 		}
257 		if (fread(state.cur_header, 1, JV3_HEADER_LEN, self->jv3_fp)
258 			< JV3_HEADER_LEN)
259 		{
260 			flush_header(self, &state);
261 			return DSK_ERR_OK;
262 		}
263 	}
264 	err = flush_header(self, &state);
265 	if (err) return err;
266 
267 	/* OK, we need to append a sector, but the existing header is
268 	 * entirely full. Create a new header and create that. */
269 	if (append && !state.stop)
270 	{
271 		memset(state.cur_header, JV3_FREE, JV3_HEADER_LEN);
272 		/* If the last block in the file is a header block,
273 		 * replace it. Otherwise, append a new header block. */
274 		if (state.data_offset > state.header_offset + JV3_HEADER_LEN)
275 		{
276 			state.header_offset = self->jv3_len;
277 			state.data_offset   = self->jv3_len + JV3_HEADER_LEN;
278 		}
279 		state.sector_head = state.cur_header;
280 		state.sector_head[0] = JV3_FREE;
281 		state.sector_head[1] = JV3_FREE;
282 		state.sector_head[2] = append;
283 		state.flags  = append;
284 		state.isfree = 1;
285 		state.secsize = decode_size(1, append);
286 
287 		err = (*cbk)(self, &state, param);
288 		if (!err)
289 		{
290 			return flush_header(self, &state);
291 		}
292 		else
293 		{
294 			flush_header(self, &state);
295 			return err;
296 		}
297 	}
298         return DSK_ERR_OK;
299 }
300 
301 
302 
303 
304 
305 /*
306 dsk_err_t dump_callback(JV3_DSK_DRIVER *self, JV3_ENUM_STATE *state,
307 		void *param)
308 {
309 	printf("cyl=%02x head=%02x sec=%02x size=%4d offset=%6ld free=%d flags=%02x\n",
310 		state->cyl, state->head, state->sector,
311 		(int)state->secsize, state->data_offset, state->isfree,
312 		state->flags);
313 	return DSK_ERR_OK;
314 }*/
315 
316 
jv3_open(DSK_DRIVER * s,const char * filename)317 dsk_err_t jv3_open(DSK_DRIVER *s, const char *filename)
318 {
319 	DC_CHECK(s);
320 
321 	self->jv3_fp = fopen(filename, "rb");
322 	if (!self->jv3_fp) return DSK_ERR_NOTME;
323 
324 	/* Get file size */
325 	if (fseek(self->jv3_fp, 0, SEEK_END) < 0        ||
326 	    ((self->jv3_len = ftell(self->jv3_fp)) < 0) ||
327 	    fseek(self->jv3_fp, 0, SEEK_SET) < 0)
328 	{
329 		return DSK_ERR_NOTME;
330 	}
331 
332 	if (fread(self->jv3_header, 1, sizeof(self->jv3_header), self->jv3_fp)
333 		< (int)sizeof(self->jv3_header))
334 	{
335 		fclose(self->jv3_fp);
336 		return DSK_ERR_NOTME;
337 	}
338 /* Diagnostic dump of what we just loaded
339 	jv3_enum_sectors(self, 0, dump_callback, NULL);
340 */
341 	/* OK, the header loaded. There's no magic number or other
342 	 * metadata we can check, so just assume it worked */
343 	return DSK_ERR_OK;
344 }
345 
jv3_creat(DSK_DRIVER * s,const char * filename)346 dsk_err_t jv3_creat(DSK_DRIVER *s, const char *filename)
347 {
348 	DC_CHECK(s);
349 
350 	self->jv3_fp = fopen(filename, "wb");
351 	if (!self->jv3_fp) return DSK_ERR_SYSERR;
352 
353 	memset(self->jv3_header, JV3_FREE, sizeof(self->jv3_header));
354 	self->jv3_len = sizeof(self->jv3_header);
355 
356 	if (fwrite(self->jv3_header, 1, sizeof(self->jv3_header), self->jv3_fp)
357 		< (int)sizeof(self->jv3_header))
358 	{
359 		return DSK_ERR_SYSERR;
360 	}
361 	return DSK_ERR_OK;
362 }
363 
364 
365 
jv3_close(DSK_DRIVER * s)366 dsk_err_t jv3_close(DSK_DRIVER *s)
367 {
368 	DC_CHECK(s);
369 
370 	if (fclose(self->jv3_fp)) return DSK_ERR_SYSERR;
371 	return DSK_ERR_OK;
372 }
373 
374 
375 
jv3_write(DSK_DRIVER * self,const DSK_GEOMETRY * geom,const void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector)376 dsk_err_t jv3_write(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
377                               const void *buf, dsk_pcyl_t cylinder,
378                               dsk_phead_t head, dsk_psect_t sector)
379 {
380 	return jv3_xwrite (self, geom, buf, cylinder, head,
381 				cylinder, head, sector, geom->dg_secsize, 0);
382 }
383 
384 
385 typedef struct
386 {
387 	int found;
388 	dsk_pcyl_t cylinder;
389 	dsk_phead_t head;
390 	dsk_psect_t sector;
391 	int deleted;
392 	int fm;
393 	size_t sector_size;
394 	void *buf;
395 	dsk_err_t result;
396 
397 } READSEC_DATA;
398 
399 
400 
401 
droptrack_callback(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * param)402 dsk_err_t droptrack_callback(JV3_DSK_DRIVER *self, JV3_ENUM_STATE *state,
403 		void *param)
404 {
405 	READSEC_DATA *rsd = (READSEC_DATA *)param;
406 
407 	if (state->cyl    == rsd->cylinder &&
408 	    state->head   == rsd->head     &&
409 	    state->isfree == 0)
410 	{
411 		state->sector_head[0] = JV3_FREE;
412 		state->sector_head[1] = JV3_FREE;
413 		state->sector_head[2] = JV3_FREEF;
414 		state->sector_head[2] |= encode_size(1, state->secsize);
415 		state->touched = 1;
416 	}
417 	return DSK_ERR_OK;
418 }
419 
420 
dropsector_callback(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * param)421 dsk_err_t dropsector_callback(JV3_DSK_DRIVER *self, JV3_ENUM_STATE *state,
422 		void *param)
423 {
424 	READSEC_DATA *rsd = (READSEC_DATA *)param;
425 
426 	if (state->cyl    == rsd->cylinder &&
427 	    state->head   == rsd->head     &&
428 	    state->sector == rsd->sector   &&
429 	    state->isfree == 0)
430 	{
431 		state->sector_head[0] = JV3_FREE;
432 		state->sector_head[1] = JV3_FREE;
433 		state->sector_head[2] = JV3_FREEF;
434 		state->sector_head[2] |= encode_size(1, state->secsize);
435 		state->touched = 1;
436 	}
437 	return DSK_ERR_OK;
438 }
439 
format_sector_callback(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * param)440 dsk_err_t format_sector_callback(JV3_DSK_DRIVER *self,
441 				 JV3_ENUM_STATE *state, void *param)
442 {
443 	READSEC_DATA *rsd = (READSEC_DATA *)param;
444 	unsigned n;
445 	long pos;
446 
447 	if (!state->isfree || state->secsize != rsd->sector_size)
448 	{
449 		return DSK_ERR_OK;
450 	}
451 	/* We've got a suitable blank space */
452 	state->touched = 1;
453 	state->sector_head[0] = rsd->cylinder;
454 	state->sector_head[1] = rsd->sector;
455 	state->sector_head[2] = encode_size(0, rsd->sector_size) & JV3_SIZE;
456 	if (!rsd->fm)  state->sector_head[2] |= JV3_DENSITY;
457 	if (rsd->head) state->sector_head[2] |= JV3_SIDE;
458 
459 	/* Write fillers */
460 	if (fseek(self->jv3_fp, state->data_offset, SEEK_SET) < 0)
461 		return DSK_ERR_SYSERR;
462 
463 	for (n = 0; n < state->secsize; n++)
464 	{
465 		if (fputc(rsd->deleted, self->jv3_fp) == EOF)
466 			return DSK_ERR_SYSERR;
467 	}
468 	pos = ftell(self->jv3_fp);
469 	/* If we have extended the file, record the fact. */
470 	if (pos > self->jv3_len)
471 	{
472 		self->jv3_len = pos;
473 	}
474 	state->stop = 1;	/* New sector written. */
475 
476 	return DSK_ERR_OK;
477 }
478 
479 
jv3_format(DSK_DRIVER * s,DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,const DSK_FORMAT * format,unsigned char filler)480 dsk_err_t jv3_format(DSK_DRIVER *s, DSK_GEOMETRY *geom,
481                                 dsk_pcyl_t cylinder, dsk_phead_t head,
482                                 const DSK_FORMAT *format, unsigned char filler)
483 {
484 	READSEC_DATA rsd;
485 	dsk_psect_t sec;
486 	dsk_err_t err;
487 	unsigned char blanksize;
488 
489 	DC_CHECK(s);
490 
491 	if (self->jv3_header[JV3_HEADER_LEN - 1] == 0) return DSK_ERR_RDONLY;
492 
493 	rsd.found = 0;
494 	rsd.cylinder = cylinder;
495 	rsd.head = head;
496 
497 	/* Delete any existing sectors */
498 	err = jv3_enum_sectors(self, 0, droptrack_callback, &rsd);
499 
500 	if (err) return err;
501 
502 	/* And write a blank set */
503 	for (sec = 0; sec < geom->dg_sectors; sec++)
504 	{
505 		rsd.found       = 0;
506 		rsd.cylinder    = cylinder;
507 		rsd.head        = head;
508 		rsd.sector      = format[sec].fmt_sector;
509 		rsd.fm          = (geom->dg_fm & RECMODE_MASK) == RECMODE_FM;
510 		rsd.sector_size = format[sec].fmt_secsize;
511 		rsd.deleted     = filler;
512 		blanksize       = JV3_FREEF | encode_size(1, rsd.sector_size);
513 		err = jv3_enum_sectors(self, blanksize, format_sector_callback, &rsd);
514 		if (err) return err;
515 	}
516 	return DSK_ERR_OK;
517 }
518 
519 
520 
jv3_secid(DSK_DRIVER * s,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,DSK_FORMAT * result)521 dsk_err_t jv3_secid(DSK_DRIVER *s, const DSK_GEOMETRY *geom,
522                                 dsk_pcyl_t cylinder, dsk_phead_t head,
523                                 DSK_FORMAT *result)
524 {
525 	DSK_FORMAT *buf = NULL;
526 	dsk_psect_t count = 0;
527 	dsk_err_t err;
528 	int offset;
529 
530 	DC_CHECK(s)
531 
532 	/* Gather all the sector IDs for a track together, and then
533 	 * pick one */
534 	err = jv3_trackids(s, geom, cylinder, head, &count, &buf);
535 
536 	if (count == 0 && !err)
537 	{
538 		self->jv3_sector = 0;
539 		err = DSK_ERR_NOADDR;
540 	}
541 	if (!err)
542 	{
543 		offset = self->jv3_sector % count;
544 
545 		*result = buf[offset];
546 
547 		++self->jv3_sector;
548 	}
549 	if (buf)
550 	{
551 		dsk_free(buf);
552 	}
553 	return err;
554 }
555 
556 
jv3_xseek(DSK_DRIVER * self,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head)557 dsk_err_t jv3_xseek(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
558                                 dsk_pcyl_t cylinder, dsk_phead_t head)
559 {
560 	DSK_FORMAT dummy;
561 
562 	dsk_err_t err = jv3_secid(self, geom, cylinder, head, &dummy);
563 
564 	if (err == DSK_ERR_NOADDR) return DSK_ERR_SEEKFAIL;
565 
566 	return err;
567 }
568 
569 
jv3_read(DSK_DRIVER * self,const DSK_GEOMETRY * geom,void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t sector)570 dsk_err_t jv3_read(DSK_DRIVER *self, const DSK_GEOMETRY *geom,
571                               void *buf, dsk_pcyl_t cylinder,
572                               dsk_phead_t head, dsk_psect_t sector)
573 {
574 	return jv3_xread(self, geom, buf, cylinder, head, cylinder,
575 		dg_x_head(geom, head),
576 		dg_x_sector(geom, head, sector), geom->dg_secsize, NULL);
577 }
578 
579 
580 
581 
xread_callback(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * param)582 static dsk_err_t xread_callback(JV3_DSK_DRIVER *self,
583 				JV3_ENUM_STATE *state, void *param)
584 {
585 	READSEC_DATA *rsd = (READSEC_DATA *)param;
586 
587 	size_t sec_size = state->secsize;
588 
589 	int fm      = (state->flags & JV3_DENSITY) ? 0 : 1;
590 	int deleted = (state->flags & JV3_DAM)     ? 1 : 0;
591 
592 	if (state->isfree                  ||
593 	    state->cyl    != rsd->cylinder ||
594  	    state->head   != rsd->head     ||
595 	    state->sector != rsd->sector   ||
596 	    fm            != rsd->fm       ||
597             deleted       != rsd->deleted) return DSK_ERR_OK;	/* No match */
598 
599 	if (fseek(self->jv3_fp, state->data_offset, SEEK_SET) < 0)
600 		return DSK_ERR_SYSERR;
601 	if (sec_size > rsd->sector_size) sec_size = rsd->sector_size;
602 
603 	sec_size = fread(rsd->buf, 1, sec_size, self->jv3_fp);
604 	for (; sec_size < rsd->sector_size; sec_size++)
605 	{
606 		((unsigned char *)(rsd->buf))[sec_size] = 0xE5;
607 	}
608 	rsd->result = (state->flags & JV3_ERROR) ? DSK_ERR_DATAERR : DSK_ERR_OK;
609 	rsd->deleted = deleted;
610 	state->stop = 1;
611 	return DSK_ERR_OK;
612 }
613 
614 
615 /* Attempt to write to an existing sector */
xwrite_callback1(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * param)616 static dsk_err_t xwrite_callback1(JV3_DSK_DRIVER *self,
617 				JV3_ENUM_STATE *state, void *param)
618 {
619 	READSEC_DATA *rsd = (READSEC_DATA *)param;
620 
621 	size_t sec_size = state->secsize;
622 
623 	int fm      = (state->flags & JV3_DENSITY) ? 0 : 1;
624 	int deleted = (state->flags & JV3_DAM)     ? 1 : 0;
625 
626 	if (state->isfree                  ||
627 	    state->cyl    != rsd->cylinder ||
628  	    state->head   != rsd->head     ||
629 	    state->sector != rsd->sector   ||
630 	    fm            != rsd->fm       ||
631             deleted       != rsd->deleted) return DSK_ERR_OK;	/* No match */
632 
633 	if (fseek(self->jv3_fp, state->data_offset, SEEK_SET) < 0)
634 		return DSK_ERR_SYSERR;
635 	if (sec_size > rsd->sector_size) sec_size = rsd->sector_size;
636 
637 	if (fwrite(rsd->buf, 1, sec_size, self->jv3_fp) < sec_size)
638 		return DSK_ERR_SYSERR;
639 
640 	if (rsd->deleted)
641 	{
642 		state->sector_head[2] &= ~JV3_DAM;
643 		state->sector_head[2] |= 0x20;
644 	}
645 	else
646 	{
647 		state->sector_head[2] &= ~JV3_DAM;
648 	}
649 	if (state->sector_head[2] != state->flags)
650 		state->touched = 1;
651 	state->stop = 1;
652 	rsd->found = 1;
653 	return DSK_ERR_OK;
654 }
655 
656 
657 
658 
659 
jv3_xread(DSK_DRIVER * s,const DSK_GEOMETRY * geom,void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_pcyl_t cyl_expected,dsk_phead_t head_expected,dsk_psect_t sector,size_t sector_size,int * deleted)660 dsk_err_t jv3_xread(DSK_DRIVER *s, const DSK_GEOMETRY *geom, void *buf,
661                               dsk_pcyl_t cylinder, dsk_phead_t head,
662                               dsk_pcyl_t cyl_expected,
663 			      dsk_phead_t head_expected,
664                               dsk_psect_t sector, size_t sector_size,
665 			      int *deleted)
666 {
667 	dsk_err_t err = DSK_ERR_OK;
668 	READSEC_DATA rsd;
669 
670 	DC_CHECK(s);
671 
672 
673 	rsd.cylinder = cylinder;
674 	rsd.head     = head;
675 	rsd.sector   = sector;
676 	if (deleted && *deleted) rsd.deleted = 1;
677 	else			 rsd.deleted = 0;
678 	rsd.fm       = (geom->dg_fm & RECMODE_MASK) == RECMODE_FM;
679 	rsd.sector_size = sector_size;
680 	rsd.buf         = buf;
681 	rsd.result      = DSK_ERR_NOADDR;
682 
683 	err = jv3_enum_sectors(self, 0, xread_callback, &rsd);
684 
685 	if (err) return err;
686 	if (rsd.result == DSK_ERR_NOADDR)
687 	{
688 		self->jv3_sector = 0;
689 	}
690 	if (deleted) *deleted = rsd.deleted;
691 	return rsd.result;
692 }
693 
jv3_xwrite(DSK_DRIVER * s,const DSK_GEOMETRY * geom,const void * buf,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_pcyl_t cyl_expected,dsk_phead_t head_expected,dsk_psect_t sector,size_t sector_size,int deleted)694 dsk_err_t jv3_xwrite(DSK_DRIVER *s, const DSK_GEOMETRY *geom, const void *buf,
695                               dsk_pcyl_t cylinder, dsk_phead_t head,
696                               dsk_pcyl_t cyl_expected, dsk_phead_t head_expected,
697                               dsk_psect_t sector, size_t sector_size, int deleted)
698 {
699 	dsk_err_t err = DSK_ERR_OK;
700 	READSEC_DATA rsd;
701 
702 	DC_CHECK(s);
703 
704 	if (self->jv3_header[JV3_HEADER_LEN - 1] == 0) return DSK_ERR_RDONLY;
705 
706 	rsd.cylinder = cylinder;
707 	rsd.head     = head;
708 	rsd.sector   = sector;
709 	rsd.deleted  = deleted;
710 	rsd.fm       = (geom->dg_fm & RECMODE_MASK) == RECMODE_FM;
711 	rsd.sector_size = sector_size;
712 	rsd.buf         = (void *)buf;
713 	rsd.result      = DSK_ERR_NOADDR;
714 	rsd.found       = 0;
715 
716 	err = jv3_enum_sectors(self, 0, xwrite_callback1, &rsd);
717 
718 	if (err) return err;
719 
720 	if (!rsd.found)
721 	{
722 /* If the sector has not been found, should we create it? Probably not; only
723  * jv3_format should create sectors if they aren't there. */
724 		self->jv3_sector = 0;
725 		return DSK_ERR_NOADDR;
726 	}
727 	return DSK_ERR_OK;
728 }
729 
730 typedef struct
731 {
732 	int fm;
733         dsk_pcyl_t cylinder;
734 	dsk_phead_t head;
735 	unsigned count;
736 	DSK_FORMAT *result;
737 } TRACKIDS_PARAM;
738 
739 
trackids_callback(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * p)740 static dsk_err_t trackids_callback(JV3_DSK_DRIVER *self,
741 				JV3_ENUM_STATE *state, void *p)
742 {
743 	TRACKIDS_PARAM *param = (TRACKIDS_PARAM *)p;
744 
745 	int fm          = (state->flags & JV3_DENSITY) ? 0 : 1;
746 
747 	if (state->cyl    == param->cylinder &&
748 	    state->head   == param->head &&
749 	    fm            == param->fm &&
750 	    state->isfree == 0)
751 	{
752 		if (param->result)
753 		{
754 			param->result[param->count].fmt_cylinder = state->cyl;
755 			param->result[param->count].fmt_head     = state->head;
756 			param->result[param->count].fmt_sector   = state->sector;
757 			param->result[param->count].fmt_secsize  = state->secsize;
758 
759 		}
760 		++param->count;
761 	}
762 	return DSK_ERR_OK;
763 }
764 
765 
766 
jv3_trackids(DSK_DRIVER * s,const DSK_GEOMETRY * geom,dsk_pcyl_t cylinder,dsk_phead_t head,dsk_psect_t * count,DSK_FORMAT ** result)767 dsk_err_t jv3_trackids(DSK_DRIVER *s, const DSK_GEOMETRY *geom,
768                             dsk_pcyl_t cylinder, dsk_phead_t head,
769                             dsk_psect_t *count, DSK_FORMAT **result)
770 {
771 	TRACKIDS_PARAM param;
772 	dsk_err_t err;
773 
774 	DC_CHECK(s);
775 
776 	param.fm   = (geom->dg_fm & RECMODE_MASK) == RECMODE_FM;
777 	param.cylinder = cylinder;
778 	param.head     = head;
779 	param.count = 0;
780 	param.result = NULL;
781 
782 	/* We call this function twice: Once to count the number of
783 	 * sectors with the given cylinder and head; then allocate the
784 	 * results array; then call again to populate the array. */
785 	err = jv3_enum_sectors(self, 0, trackids_callback, &param);
786 	if (err) return err;
787 
788 	if (!param.count)
789 	{
790 		*count = 0;
791 		*result = NULL;
792 		return DSK_ERR_OK;
793 	}
794 	param.result = dsk_malloc(param.count * sizeof(DSK_FORMAT));
795 	if (!param.result) return DSK_ERR_NOMEM;
796 
797 	param.count = 0;
798 	err = jv3_enum_sectors(self, 0, trackids_callback, &param);
799 	if (err) return err;
800 
801 	*count = param.count;
802 	*result = param.result;
803 
804 	return DSK_ERR_OK;
805 }
806 
jv3_status(DSK_DRIVER * s,const DSK_GEOMETRY * geom,dsk_phead_t head,unsigned char * result)807 dsk_err_t jv3_status(DSK_DRIVER *s, const DSK_GEOMETRY *geom,
808                   dsk_phead_t head, unsigned char *result)
809 {
810 	DC_CHECK(s);
811 
812 	if (!self->jv3_fp)
813 		*result &= ~DSK_ST3_READY;
814 
815 	if (self->jv3_header[JV3_HEADER_LEN - 1] == 0)
816 		*result |= DSK_ST3_RO;
817 
818 	return DSK_ERR_OK;
819 }
820 
821 
822 typedef struct
823 {
824 	DSK_GEOMETRY testgeom;
825         dsk_psect_t minsec0, maxsec0, minsec1, maxsec1;
826 } GEOM_RESULT;
827 
geom_callback(JV3_DSK_DRIVER * self,JV3_ENUM_STATE * state,void * param)828 static dsk_err_t geom_callback(JV3_DSK_DRIVER *self,
829 				JV3_ENUM_STATE *state, void *param)
830 {
831 	GEOM_RESULT *gr = (GEOM_RESULT *)param;
832 
833 	/* Ignore free sectors */
834 	if (state->isfree) return DSK_ERR_OK;
835 
836 	gr->testgeom.dg_secsize = state->secsize;
837 	if (state->cyl >= gr->testgeom.dg_cylinders)
838 		gr->testgeom.dg_cylinders = state->cyl + 1;
839 	if (state->head >= gr->testgeom.dg_heads)
840 		gr->testgeom.dg_heads = state->head + 1;
841 	gr->testgeom.dg_datarate = RATE_SD;
842 	gr->testgeom.dg_fm = (state->flags & JV3_DENSITY) ? RECMODE_MFM : RECMODE_FM;
843 	if (state->head == 1)
844 	{
845 		if (state->sector < gr->minsec1) gr->minsec1 = state->sector;
846 		if (state->sector > gr->maxsec1) gr->maxsec1 = state->sector;
847 	}
848 	else
849 	{
850 		if (state->sector < gr->minsec0) gr->minsec0 = state->sector;
851 		if (state->sector > gr->maxsec0) gr->maxsec0 = state->sector;
852 	}
853 	return DSK_ERR_OK;
854 }
855 
856 
jv3_getgeom(DSK_DRIVER * s,DSK_GEOMETRY * geom)857 dsk_err_t jv3_getgeom(DSK_DRIVER *s, DSK_GEOMETRY *geom)
858 {
859 	dsk_err_t err;
860 	GEOM_RESULT result;
861 
862 	DC_CHECK(s);
863 
864 	/* First try the normal geometry probe */
865 	err = dsk_defgetgeom(s, geom);
866 
867 	if (!err) return err;
868 
869         dg_stdformat(&result.testgeom, FMT_180K, NULL, NULL);
870         result.testgeom.dg_cylinders = 0;
871         result.testgeom.dg_sectors = 0;
872         result.testgeom.dg_heads = 0;
873 
874         result.minsec0 = result.minsec1 = 256;
875         result.maxsec0 = result.maxsec1 = 0;
876 
877 	err = jv3_enum_sectors(self, 0, geom_callback, &result);
878 
879 	if (err) return err;
880 
881         result.testgeom.dg_secbase = result.minsec0;
882 	result.testgeom.dg_sectors = (result.maxsec0 - result.minsec0) + 1;
883 
884 	if (result.testgeom.dg_cylinders == 0 ||
885             result.testgeom.dg_sectors   == 0) return DSK_ERR_BADFMT;
886 
887         memcpy(geom, &result.testgeom, sizeof(*geom));
888         return DSK_ERR_OK;
889 
890 }
891 
892 
893