1 /*
2  * NIBTOOL write routines
3  * Copyright 2005-2011 C64 Preservation Project
4  * based on MNIB by Markus Brenner <markus(at)brenner(dot)de>
5  */
6 
7 #include <stdio.h>
8 #include <stdlib.h>
9 #include <string.h>
10 #include <time.h>
11 
12 #include "mnibarch.h"
13 #include "gcr.h"
14 #include "nibtools.h"
15 
16 void
master_track(CBM_FILE fd,BYTE * track_buffer,BYTE * track_density,int track,size_t tracklen)17 master_track(CBM_FILE fd, BYTE *track_buffer, BYTE *track_density, int track, size_t tracklen)
18 {
19 	int i, leader =  0x20;
20 	static size_t skewbytes = 0;
21 	static BYTE last_density = -1;
22 	BYTE rawtrack[NIB_TRACK_LENGTH * 2];
23 	BYTE tempfillbyte;
24 
25 	/* loop last byte of track data for filler */
26 	if(fillbyte == 0xfe) /* $fe is special case for loop */
27 		tempfillbyte = track_buffer[(track * NIB_TRACK_LENGTH) + tracklen - 1];
28 	else
29 		tempfillbyte = fillbyte;
30 
31 	if(verbose>2) printf("(fill:$%.2x)",tempfillbyte);
32 
33 	if(track_density[track] & BM_NO_SYNC)
34 		memset(rawtrack, 0x55, sizeof(rawtrack));
35 	else
36 		memset(rawtrack, tempfillbyte, sizeof(rawtrack));
37 
38 	/* apply skew, if specified */
39 	if(skew)
40 	{
41 		skewbytes += skew * (capacity[track_density[track]&3] / 200);
42 
43 		if(skewbytes > NIB_TRACK_LENGTH)
44 			skewbytes = skewbytes - NIB_TRACK_LENGTH;
45 
46 		if(verbose>1) printf("{skew=%d}", skewbytes);
47 	}
48 
49 	/* check for and correct initial too short sync mark */
50 	//if( ((!(track_density[track] & BM_NO_SYNC)) &&
51 	//	    (track_buffer[track * NIB_TRACK_LENGTH] == 0xff) &&
52 	//	    (track_buffer[(track * NIB_TRACK_LENGTH) + 1] != 0xff)) || (presync) )
53 	if(presync)
54 	{
55 		if(verbose>1) printf("{presync}");
56 		memset(rawtrack + leader + skewbytes - 2, 0xff, 2);
57 	}
58 
59 	/* merge track data */
60 	memcpy(rawtrack + leader + skewbytes,  track_buffer + (track * NIB_TRACK_LENGTH), tracklen);
61 
62 	//printf("[%.2x%.2x%.2x%.2x%.2x] ",
63 	//		rawtrack[0], rawtrack[1], rawtrack[2], rawtrack[3], rawtrack[4]);
64 
65 	/* handle short tracks */
66 	if(tracklen < capacity[track_density[track]&3])
67 	{
68 			if(verbose>1) printf("[pad:%d]", capacity[track_density[track]&3] - tracklen);
69 			tracklen = capacity[track_density[track]&3];
70 	}
71 
72 	/* "fix" for track 18 mastering */
73 	if(track==18*2)
74 		memcpy(rawtrack + tracklen - 5, "UJMSU", 5);
75 
76 	/* replace 0x00 bytes by 0x01, as 0x00 indicates end of track */
77 	if(!use_floppycode_srq)  // not in srq code
78 		replace_bytes(rawtrack, sizeof(rawtrack), 0x00, 0x01);
79 
80 	/* step to destination track and set density */
81 	step_to_halftrack(fd, track);
82 	if((track_density[track]&3) != last_density)
83 	{
84 		set_density(fd, track_density[track]&3);
85 		if(verbose>2) printf("[D]");
86 		last_density = track_density[track]&3;
87 	}
88 
89 	/* this doesn't work over USB since we aren't in control of timing, I don't think */
90 	// try to do track alignment through simple timers
91 	if((align_disk) && (auto_capacity_adjust))
92 	{
93 		/* subtract overhead from one revolution;
94 		    adjust for motor speed and density;	*/
95 		align_delay = (int) ((175500) + ((300 - motor_speed) * 600));
96 		msleep(align_delay);
97 	}
98 
99 	/* burst send track */
100 	for (i = 0; i < 3; i ++)
101 	{
102 		send_mnib_cmd(fd, FL_WRITE, NULL, 0);
103 
104 		/* IHS will lock forever if IHS is set and it sees no index hole, i.e. side 2 of flippy disk or there is no compatible IHS */
105 		/* Arnd has some code to test for it, not implemented yet */
106 		burst_write(fd, (unsigned char)((ihs) ? 0x00 : 0x03));
107 
108 		/* align disk waits until end of sync before writing */
109 		burst_write(fd, (unsigned char)((align_disk) ? 0xfb : 0x00));
110 
111 		if (burst_write_track(fd, rawtrack, (int)(tracklen + leader + skewbytes + 1)))
112 			break;
113 		else
114 		{
115 			//putchar('?');
116 			printf("(timeout) ");
117 			fflush(stdin);
118 			burst_read(fd);
119 			//msleep(500);
120 			//printf("%c ", test_par_port(fd)? '+' : '-');
121 			test_par_port(fd);
122 		}
123 	}
124 
125 	if(i == 3)
126 	{
127 		printf("\n\nNo good write of track due to timeouts.  Aborting!\n");
128 		exit(1);
129 	}
130 }
131 
132 void
master_disk(CBM_FILE fd,BYTE * track_buffer,BYTE * track_density,size_t * track_length)133 master_disk(CBM_FILE fd, BYTE *track_buffer, BYTE *track_density, size_t *track_length)
134 {
135 	int track, verified, retries, added_sync = 0;
136 	size_t badgcr, length, verlen, verlen2;
137 	BYTE verbuf1[NIB_TRACK_LENGTH], verbuf2[NIB_TRACK_LENGTH], verbuf3[NIB_TRACK_LENGTH], align;
138 	size_t gcr_diff;
139 	char errorstring[0x1000];
140 
141 	for (track = start_track; track <= end_track; track += track_inc)
142 	{
143 		/* double-check our sync-flag assumptions and process track for remaster */
144 		track_density[track] =
145 			check_sync_flags(track_buffer + (track * NIB_TRACK_LENGTH), track_density[track], track_length[track]);
146 
147 		/* engineer killer track */
148 		if(track_density[track] & BM_FF_TRACK)
149 		{
150 				kill_track(fd, track);
151 				if(verbose) printf("\n%4.1f: KILLED!",  (float) track / 2);
152 				continue;
153 		}
154 
155 		/* zero out empty tracks entirely */
156 		if(!check_formatted(track_buffer + (track * NIB_TRACK_LENGTH), track_length[track]))
157 		{
158 				zero_track(fd, track);
159 				if(verbose) printf("\n%4.1f: UNFORMATTED!",  (float) track / 2);
160 				continue;
161 		}
162 
163 		/* user display */
164 		if(verbose)
165 		{
166 			printf("\n%4.1f: (", (float)track/2);
167 			printf("%d", track_density[track]&3);
168 			if ( (track_density[track]&3) != speed_map[track/2]) printf("!");
169 			printf(":%d) ", track_length[track]);
170 			if (track_density[track] & BM_NO_SYNC) printf("NOSYNC ");
171 			if (track_density[track] & BM_FF_TRACK) printf("KILLER ");
172 			printf("WRITE ");
173 		}
174 
175 		badgcr = check_bad_gcr(track_buffer + (track * NIB_TRACK_LENGTH), track_length[track]);
176 
177 		if(increase_sync)
178 		{
179 			added_sync = lengthen_sync(track_buffer + (track * NIB_TRACK_LENGTH),
180 				track_length[track], NIB_TRACK_LENGTH);
181 
182 			track_length[track] += added_sync;
183 		}
184 
185 		if(increase_sync) { if(verbose) printf("[+sync:%d]", added_sync); }
186 		if(badgcr) { if(verbose) printf("[weak:%d]", badgcr); }
187 
188 		length = compress_halftrack(track, track_buffer + (track * NIB_TRACK_LENGTH),
189 			track_density[track], track_length[track]);
190 
191 		master_track(fd, track_buffer, track_density, track, length);
192 
193 		if(track_match)	// Try to verify our write
194 		{
195 			verified=retries=0;
196 			while(!verified)
197 			{
198 				// Don't bother to compare unformatted or bad data
199 				if (track_length[track] == NIB_TRACK_LENGTH) break;
200 
201 				memset(verbuf1, 0, NIB_TRACK_LENGTH);
202 				if((ihs) && (!(track_density[track] & BM_NO_SYNC)))
203 					send_mnib_cmd(fd, FL_READIHS, NULL, 0);
204 				else if (Use_SCPlus_IHS) // "-j"
205 					send_mnib_cmd(fd, FL_IHS_READ_SCP, NULL, 0);
206 				else
207 				{
208 					if ((track_density[track] & BM_NO_SYNC) || (track_density[track] & BM_FF_TRACK))
209 						send_mnib_cmd(fd, FL_READWOSYNC, NULL, 0);
210 					else
211 						send_mnib_cmd(fd, FL_READNORMAL, NULL, 0);
212 				}
213 				burst_read(fd);
214 				burst_read_track(fd, verbuf1, NIB_TRACK_LENGTH);
215 
216 				memset(verbuf2, 0, NIB_TRACK_LENGTH);
217 				memset(verbuf3, 0, NIB_TRACK_LENGTH);
218 				verlen   = extract_GCR_track(verbuf2, verbuf1, &align, track/2, track_length[track], track_length[track]);
219 				verlen2 = extract_GCR_track(verbuf3, track_buffer+(track * NIB_TRACK_LENGTH), &align, track/2, track_length[track], track_length[track]);
220 
221 				if(verbose) printf("\n      (%d:%d) VERIF", track_density[track]&3, verlen);
222 				fprintf(fplog, "\n      (%d:%d) VERIF", track_density[track]&3, verlen);
223 
224 				// Fix bad GCR in tracks for compare
225 				badgcr = check_bad_gcr(verbuf2, track_length[track]);
226 				if(verbose>1) printf("(badgcr=%.4d:", badgcr);
227 				badgcr = check_bad_gcr(verbuf3, track_length[track]);
228 				if(verbose>1) printf("%.4d)", badgcr);
229 
230 				// compare raw gcr data
231 				gcr_diff = compare_tracks(verbuf3, verbuf2, verlen, verlen, 1, errorstring);
232 				if(verbose) printf(" (diff:%.4d) ", (int)gcr_diff);
233 				fprintf(fplog, " (diff:%.4d) ", (int)gcr_diff);
234 
235 				if(gcr_diff <= (size_t)sector_map[track/2]+10)
236 				{
237 					printf("OK ");
238 					verified=1;
239 				}
240 				else
241 				{
242 					retries++;
243 					printf("Retry %d ", retries);
244 					zero_track(fd, track);
245 					master_track(fd, track_buffer, track_density, track, length);
246 				}
247 				if(((track>70)&&(retries>=3))||(retries>=10))
248 				{
249 					printf("\n      Write verify FAILED - Odd data or bad media! ");
250 					verified=1;
251 				}
252 			}
253 		}
254 	}
255 }
256 
257 void
master_disk_raw(CBM_FILE fd,BYTE * track_buffer,BYTE * track_density,size_t * track_length)258 master_disk_raw(CBM_FILE fd, BYTE *track_buffer, BYTE *track_density, size_t *track_length)
259 {
260 	int track, density;
261 	BYTE trackbuf[NIB_TRACK_LENGTH];
262 	char testfilename[16];
263 	FILE *trkin = '\0';
264 	size_t length;
265 
266 	for (track = start_track; track <= end_track; track += track_inc)
267 	{
268 		printf("\n%4.1f:", (float) track / 2);
269 
270 		// read in raw track at density (in filename)
271 		for (density = 3; density >= 0; density--)
272 		{
273 			sprintf(testfilename, "raw/tr%.1fd%d", (float) track/2, density);
274 
275 			if( (trkin = fopen(testfilename, "rb")) )
276 			{
277 				if(verbose) printf(" [%s] ", testfilename);
278 				break;
279 			}
280 		}
281 
282 		if (trkin)
283 		{
284 			/* erase mem and grab data from file */
285 			memset(trackbuf, 0x00, sizeof(trackbuf));
286 			fseek(trkin, 0, SEEK_END);
287 			length = ftell(trkin);
288 			rewind(trkin);
289 			fread(trackbuf, length, 1, trkin); // @@@SRT: check success
290 			fclose(trkin);
291 
292 			if(length == 0)
293 				length = NIB_TRACK_LENGTH;
294 
295 			/* process track */
296 			memcpy(track_buffer + (track * NIB_TRACK_LENGTH), trackbuf, NIB_TRACK_LENGTH);
297 			track_density[track] = check_sync_flags(track_buffer + (track * NIB_TRACK_LENGTH), density, length);
298 			//length = compress_halftrack(track, track_buffer + (track * NIB_TRACK_LENGTH), track_density[track], length);
299 
300 			printf(" (%d", track_density[track] & 3);
301 			if ( (track_density[track]&3) != speed_map[track/2])
302 				printf("!=%d", speed_map[track/2]);
303 			if (track_density[track] & BM_NO_SYNC)
304 					printf(":NOSYNC");
305 			else if (track_density[track] & BM_FF_TRACK)
306 				printf(":KILLER");
307 			printf(") (%d) ", length);
308 
309 			/* truncate the end if needed (reduce tail) */
310 			if (length > capacity[density & 3])
311 			{
312 				printf(" (trunc:%d) ",  length - capacity[density & 3]);
313 				length = capacity[density & 3];
314 			}
315 			master_track(fd, track_buffer, track_density, track, length);
316 		}
317 		else
318 			printf(" [missing track file - skipped]");
319 	}
320 }
321 
322 void
unformat_disk(CBM_FILE fd)323 unformat_disk(CBM_FILE fd)
324 {
325 	/* this routine writes all 1's and all 0's alternatively to try to both
326 		fix old media into working again, and wiping all data
327 	*/
328 	int track, i;
329 
330 	motor_on(fd);
331 	set_density(fd, 2);
332 
333 	printf("\nUnformatting...\n\n");
334 
335 	for (track = start_track; track <= end_track; track ++)
336 	{
337 		for(i=0;i<unformat_passes; i++)
338 		{
339 			kill_track(fd,track);
340 			zero_track(fd, track);
341 		}
342 		if(verbose) printf("\n%4.1f: UNFORMATTED!",  (float) track/2);
343 	}
344 }
345 
kill_track(CBM_FILE fd,int track)346 void kill_track(CBM_FILE fd, int track)
347 {
348 	// step head
349 	step_to_halftrack(fd, track);
350 
351 	// write all $ff bytes
352 	send_mnib_cmd(fd, FL_FILLTRACK, NULL, 0);
353 	burst_write(fd, 0xff);  // 0xff byte is all sync "killer" track
354 	burst_read(fd);
355 }
356 
357 void
zero_track(CBM_FILE fd,int track)358 zero_track(CBM_FILE fd, int track)
359 {
360 	// step head
361 	step_to_halftrack(fd, track);
362 
363 	// write all $0 bytes
364 	send_mnib_cmd(fd, FL_FILLTRACK, NULL, 0);
365 	burst_write(fd, 0x0);  // 0x00 byte is "unformatted"
366 	burst_read(fd);
367 }
368 
speed_adjust(CBM_FILE fd)369 void speed_adjust(CBM_FILE fd)
370 {
371 	int i, cap;
372 
373 	printf("\nTesting drive motor speed for 100 loops.\n");
374 	printf("--------------------------------------------------\n");
375 
376 	motor_on(fd);
377 	step_to_halftrack(fd, start_track);
378 	set_bitrate(fd, 2);
379 
380 	for (i=0; i<100; i++)
381 	{
382 		cap = track_capacity(fd);
383 		printf("Speed = %.2frpm\n", DENSITY2 / cap);
384 	}
385 
386 }
387 
388 /* This routine measures track capacity at all densities */
adjust_target(CBM_FILE fd)389 void adjust_target(CBM_FILE fd)
390 {
391 	int i, j;
392 	int cap[DENSITY_SAMPLES];
393 	int cap_high[4], cap_low[4], cap_margin[4];
394 	int run_total;
395 	int capacity_margin = 0;
396 	BYTE track_dens[4] = { 35*2, 30*2, 24*2, 17*2 };
397 
398 	printf("\nTesting track capacity at each density\n");
399 	printf("--------------------------------------------------\n");
400 
401 	for (i = 0; i <= 3; i++)
402 	{
403 		cap_high[i] = 0;
404 		cap_low[i] = 0xffff;
405 
406 		if( (start_track < track_dens[i]) && (end_track > track_dens[i]))
407 			step_to_halftrack(fd, track_dens[i]);
408 		else
409 			step_to_halftrack(fd, start_track);
410 
411 		set_bitrate(fd, (BYTE)i);
412 
413 		printf("Density %d: ", i);
414 
415 		for(j = 0, run_total = 0; j < DENSITY_SAMPLES; j++)
416 		{
417 			cap[j] = track_capacity(fd);
418 			printf("%d ", cap[j]);
419 			run_total += cap[j];
420 			if(cap[j] > cap_high[i]) cap_high[i] = cap[j];
421 			if(cap[j] < cap_low[i]) cap_low[i] = cap[j];
422 		}
423 		capacity[i] = run_total / DENSITY_SAMPLES ;
424 		cap_margin[i] = cap_high[i] - cap_low[i];
425 
426 		if(cap_margin[i] > capacity_margin)
427 			capacity_margin = cap_margin[i];
428 
429 		switch(i)
430 		{
431 			case 0:
432 				printf("(%.2frpm) margin:%d\n", DENSITY0 / capacity[0], cap_margin[i]);
433 				break;
434 
435 			case 1:
436 				printf("(%.2frpm) margin:%d\n", DENSITY1 / capacity[1], cap_margin[i]);
437 				break;
438 
439 			case 2:
440 				printf("(%.2frpm) margin:%d\n", DENSITY2 / capacity[2], cap_margin[i]);
441 				break;
442 
443 			case 3:
444 				printf("(%.2frpm) margin:%d\n", DENSITY3 / capacity[3], cap_margin[i]);
445 				break;
446 		}
447 
448 		capacity[i] -= capacity_margin + extra_capacity_margin;
449 	}
450 
451 	motor_speed = (float)( (DENSITY3 / (capacity[3] + capacity_margin + extra_capacity_margin)) +
452 										   (DENSITY2 / (capacity[2] + capacity_margin + extra_capacity_margin)) +
453 										   (DENSITY1 / (capacity[1] + capacity_margin + extra_capacity_margin)) +
454 										   (DENSITY0 / (capacity[0] + capacity_margin + extra_capacity_margin)) ) / 4;
455 
456 	printf("--------------------------------------------------\n");
457 	printf("Drive motor speed average: %.2f RPM.\n", motor_speed);
458 	printf("Track capacity margin: %d\n", capacity_margin + extra_capacity_margin);
459 
460 	if( (motor_speed > 320) || (motor_speed < 280))
461 	{
462 		printf("\n\nERROR!\nDrive speed out of range.\nCheck motor, write-protect, or bad media.\n");
463 		exit(0);
464 	}
465 }
466 
467 void
init_aligned_disk(CBM_FILE fd)468 init_aligned_disk(CBM_FILE fd)
469 {
470 	int track;
471 
472 	/* write all 0x55 */
473 	printf("\nErasing tracks...\n");
474 	for (track = start_track; track <= end_track; track += track_inc)
475 	{
476 		// step head
477 		step_to_halftrack(fd, track);
478 
479 		// write all $55 bytes
480 		send_mnib_cmd(fd, FL_FILLTRACK, NULL, 0);
481 		burst_write(fd, 0x55);
482 		burst_read(fd);
483 	}
484 
485 	/* drive code version, timers can hang w/o interrupts too long */
486 	printf("Sync sweep...\n");
487 	send_mnib_cmd(fd, FL_ALIGNDISK, NULL, 0);
488 	burst_write(fd, 0);
489 	burst_read(fd);
490 	printf("Attempted sweep-aligned tracks\n");
491 }
492