1 /*
2  *  1541d64.cpp - 1541 emulation in .d64 file
3  *
4  *  Frodo (C) 1994-1997,2002 Christian Bauer
5  *
6 
7  *
8  * Incompatibilities:
9  * ------------------
10  *
11  *  - Only read accesses possible
12  *  - Not all commands implemented
13  *  - The .d64 error info is read, but unused
14  */
15 
16 #include "sysdeps.h"
17 
18 #include "1541d64.h"
19 #include "IEC.h"
20 #include "Prefs.h"
21 
22 
23 // Channel modes (IRC users listen up :-)
24 enum {
25 	CHMOD_FREE,			// Channel free
26 	CHMOD_COMMAND,		// Command/error channel
27 	CHMOD_DIRECTORY,	// Reading directory
28 	CHMOD_FILE,			// Sequential file open
29 	CHMOD_DIRECT		// Direct buffer access ('#')
30 };
31 
32 // Access modes
33 enum {
34 	FMODE_READ, FMODE_WRITE, FMODE_APPEND
35 };
36 
37 // File types
38 enum {
39 	FTYPE_PRG, FTYPE_SEQ, FTYPE_USR, FTYPE_REL
40 };
41 
42 // Number of tracks/sectors
43 const int NUM_TRACKS = 35;
44 const int NUM_SECTORS = 683;
45 
46 // Prototypes
47 static bool match(uint8 *p, uint8 *n);
48 
49 
50 /*
51  *  Constructor: Prepare emulation, open .d64 file
52  */
53 
D64Drive(IEC * iec,char * filepath)54 D64Drive::D64Drive(IEC *iec, char *filepath) : Drive(iec)
55 {
56 	the_file = NULL;
57 	ram = NULL;
58 
59 	Ready = false;
60 	strcpy(orig_d64_name, filepath);
61 	for (int i=0; i<=14; i++) {
62 		chan_mode[i] = CHMOD_FREE;
63 		chan_buf[i] = NULL;
64 	}
65 	chan_mode[15] = CHMOD_COMMAND;
66 
67 	// Open .d64 file
68 	open_close_d64_file(filepath);
69 	if (the_file != NULL) {
70 
71 		// Allocate 1541 RAM
72 		ram = new uint8[0x800];
73 		bam = (BAM *)(ram + 0x700);
74 
75 		Reset();
76 		Ready = true;
77 	}
78 }
79 
80 
81 /*
82  *  Destructor
83  */
84 
~D64Drive()85 D64Drive::~D64Drive()
86 {
87 	// Close .d64 file
88 	open_close_d64_file("");
89 
90 	delete[] ram;
91 	Ready = false;
92 }
93 
94 
95 /*
96  *  Open/close the .d64 file
97  */
98 
open_close_d64_file(char * d64name)99 void D64Drive::open_close_d64_file(char *d64name)
100 {
101 	long size;
102 	uint8 magic[4];
103 
104 	// Close old .d64, if open
105 	if (the_file != NULL) {
106 		close_all_channels();
107 		fclose(the_file);
108 		the_file = NULL;
109 	}
110 
111 	// Open new .d64 file
112 	if (d64name[0]) {
113 		if ((the_file = fopen(d64name, "rb")) != NULL) {
114 
115 			// Check length
116 			fseek(the_file, 0, SEEK_END);
117 			if ((size = ftell(the_file)) < NUM_SECTORS * 256) {
118 				fclose(the_file);
119 				the_file = NULL;
120 				return;
121 			}
122 
123 			// x64 image?
124 			rewind(the_file);
125 			fread(&magic, 4, 1, the_file);
126 			if (magic[0] == 0x43 && magic[1] == 0x15 && magic[2] == 0x41 && magic[3] == 0x64)
127 				image_header = 64;
128 			else
129 				image_header = 0;
130 
131 			// Preset error info (all sectors no error)
132 			memset(error_info, 1, NUM_SECTORS);
133 
134 			// Load sector error info from .d64 file, if present
135 			if (!image_header && size == NUM_SECTORS * 257) {
136 				fseek(the_file, NUM_SECTORS * 256, SEEK_SET);
137 				fread(&error_info, NUM_SECTORS, 1, the_file);
138 			}
139 		}
140 	}
141 }
142 
143 
144 /*
145  *  Open channel
146  */
147 
Open(int channel,char * filename)148 uint8 D64Drive::Open(int channel, char *filename)
149 {
150 	set_error(ERR_OK);
151 
152 	// Channel 15: execute file name as command
153 	if (channel == 15) {
154 		execute_command(filename);
155 		return ST_OK;
156 	}
157 
158 	if (chan_mode[channel] != CHMOD_FREE) {
159 		set_error(ERR_NOCHANNEL);
160 		return ST_OK;
161 	}
162 
163 	if (filename[0] == '$')
164 		if (channel)
165 			return open_file_ts(channel, 18, 0);
166 		else
167 			return open_directory(filename+1);
168 
169 	if (filename[0] == '#')
170 		return open_direct(channel, filename);
171 
172 	return open_file(channel, filename);
173 }
174 
175 
176 /*
177  *  Open file
178  */
179 
open_file(int channel,char * filename)180 uint8 D64Drive::open_file(int channel, char *filename)
181 {
182 	char plainname[256];
183 	int filemode = FMODE_READ;
184 	int filetype = FTYPE_PRG;
185 	int track, sector;
186 
187 	convert_filename(filename, plainname, &filemode, &filetype);
188 
189 	// Channel 0 is READ PRG, channel 1 is WRITE PRG
190 	if (!channel) {
191 		filemode = FMODE_READ;
192 		filetype = FTYPE_PRG;
193 	}
194 	if (channel == 1) {
195 		filemode = FMODE_WRITE;
196 		filetype = FTYPE_PRG;
197 	}
198 
199 	// Allow only read accesses
200 	if (filemode != FMODE_READ) {
201 		set_error(ERR_WRITEPROTECT);
202 		return ST_OK;
203 	}
204 
205 	// Find file in directory and open it
206 	if (find_file(plainname, &track, &sector))
207 		return open_file_ts(channel, track, sector);
208 	else
209 		set_error(ERR_FILENOTFOUND);
210 
211 	return ST_OK;
212 }
213 
214 
215 /*
216  *  Analyze file name, get access mode and type
217  */
218 
convert_filename(char * srcname,char * destname,int * filemode,int * filetype)219 void D64Drive::convert_filename(char *srcname, char *destname, int *filemode, int *filetype)
220 {
221 	char *p;
222 
223 	// Search for ':', p points to first character after ':'
224 	if ((p = strchr(srcname, ':')) != NULL)
225 		p++;
226 	else
227 		p = srcname;
228 
229 	// Remaining string -> destname
230 	strncpy(destname, p, NAMEBUF_LENGTH);
231 
232 	// Search for ','
233 	p = destname;
234 	while (*p && (*p != ',')) p++;
235 
236 	// Look for mode parameters seperated by ','
237 	p = destname;
238 	while ((p = strchr(p, ',')) != NULL) {
239 
240 		// Cut string after the first ','
241 		*p++ = 0;
242 
243 		switch (*p) {
244 			case 'P':
245 				*filetype = FTYPE_PRG;
246 				break;
247 			case 'S':
248 				*filetype = FTYPE_SEQ;
249 				break;
250 			case 'U':
251 				*filetype = FTYPE_USR;
252 				break;
253 			case 'L':
254 				*filetype = FTYPE_REL;
255 				break;
256 			case 'R':
257 				*filemode = FMODE_READ;
258 				break;
259 			case 'W':
260 				*filemode = FMODE_WRITE;
261 				break;
262 			case 'A':
263 				*filemode = FMODE_APPEND;
264 				break;
265 		}
266 	}
267 }
268 
269 
270 /*
271  *  Search file in directory, find first track and sector
272  *  false: not found, true: found
273  */
274 
find_file(char * filename,int * track,int * sector)275 bool D64Drive::find_file(char *filename, int *track, int *sector)
276 {
277 	int i, j;
278 	uint8 *p, *q;
279 	DirEntry *de;
280 
281 	// Scan all directory blocks
282 	dir.next_track = bam->dir_track;
283 	dir.next_sector = bam->dir_sector;
284 
285 	while (dir.next_track) {
286 		if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
287 			return false;
288 
289 		// Scan all 8 entries of a block
290 		for (j=0; j<8; j++) {
291 			de = &dir.entry[j];
292 			*track = de->track;
293 			*sector = de->sector;
294 
295 			if (de->type) {
296 				p = (uint8 *)filename;
297 				q = de->name;
298 				for (i=0; i<16 && *p; i++, p++, q++) {
299 					if (*p == '*')	// Wildcard '*' matches all following characters
300 						return true;
301 					if (*p != *q) {
302 						if (*p != '?') goto next_entry;	// Wildcard '?' matches single character
303 						if (*q == 0xa0) goto next_entry;
304 					}
305 				}
306 
307 				if (i == 16 || *q == 0xa0)
308 					return true;
309 			}
310 next_entry: ;
311 		}
312 	}
313 
314 	return false;
315 }
316 
317 
318 /*
319  *  Open file given track/sector of first block
320  */
321 
open_file_ts(int channel,int track,int sector)322 uint8 D64Drive::open_file_ts(int channel, int track, int sector)
323 {
324 	chan_buf[channel] = new uint8[256];
325 	chan_mode[channel] = CHMOD_FILE;
326 
327 	// On the next call to Read, the first block will be read
328 	chan_buf[channel][0] = track;
329 	chan_buf[channel][1] = sector;
330 	buf_len[channel] = 0;
331 
332 	return ST_OK;
333 }
334 
335 
336 /*
337  *  Prepare directory as BASIC program (channel 0)
338  */
339 
340 const char type_char_1[] = "DSPUREERSELQGRL?";
341 const char type_char_2[] = "EERSELQGRL??????";
342 const char type_char_3[] = "LQGRL???????????";
343 
344 // Return true if name 'n' matches pattern 'p'
match(uint8 * p,uint8 * n)345 static bool match(uint8 *p, uint8 *n)
346 {
347 	if (!*p)		// Null pattern matches everything
348 		return true;
349 
350 	do {
351 		if (*p == '*')	// Wildcard '*' matches all following characters
352 			return true;
353 		if ((*p != *n) && (*p != '?'))	// Wildcard '?' matches single character
354 			return false;
355 		p++; n++;
356 	} while (*p);
357 
358 	return *n == 0xa0;
359 }
360 
open_directory(char * pattern)361 uint8 D64Drive::open_directory(char *pattern)
362 {
363 	int i, j, n, m;
364 	uint8 *p, *q;
365 	DirEntry *de;
366 	uint8 c;
367 	char *tmppat;
368 
369 	// Special treatment for "$0"
370 	if (pattern[0] == '0' && pattern[1] == 0)
371 		pattern += 1;
372 
373 	// Skip everything before the ':' in the pattern
374 	if ((tmppat = strchr(pattern, ':')) != NULL)
375 		pattern = tmppat + 1;
376 
377 	p = buf_ptr[0] = chan_buf[0] = new uint8[8192];
378 	chan_mode[0] = CHMOD_DIRECTORY;
379 
380 	// Create directory title
381 	*p++ = 0x01;	// Load address $0401 (from PET days :-)
382 	*p++ = 0x04;
383 	*p++ = 0x01;	// Dummy line link
384 	*p++ = 0x01;
385 	*p++ = 0;		// Drive number (0) as line number
386 	*p++ = 0;
387 	*p++ = 0x12;	// RVS ON
388 	*p++ = '\"';
389 
390 	q = bam->disk_name;
391 	for (i=0; i<23; i++) {
392 		if ((c = *q++) == 0xa0)
393 			*p++ = ' ';		// Replace 0xa0 by space
394 		else
395 			*p++ = c;
396 	}
397 	*(p-7) = '\"';
398 	*p++ = 0;
399 
400 	// Scan all directory blocks
401 	dir.next_track = bam->dir_track;
402 	dir.next_sector = bam->dir_sector;
403 
404 	while (dir.next_track) {
405 		if (!read_sector(dir.next_track, dir.next_sector, (uint8 *) &dir.next_track))
406 			return ST_OK;
407 
408 		// Scan all 8 entries of a block
409 		for (j=0; j<8; j++) {
410 			de = &dir.entry[j];
411 
412 			if (de->type && match((uint8 *)pattern, de->name)) {
413 				*p++ = 0x01; // Dummy line link
414 				*p++ = 0x01;
415 
416 				*p++ = de->num_blocks_l; // Line number
417 				*p++ = de->num_blocks_h;
418 
419 				*p++ = ' ';
420 				n = (de->num_blocks_h << 8) + de->num_blocks_l;
421 				if (n<10) *p++ = ' ';
422 				if (n<100) *p++ = ' ';
423 
424 				*p++ = '\"';
425 				q = de->name;
426 				m = 0;
427 				for (i=0; i<16; i++) {
428 					if ((c = *q++) == 0xa0) {
429 						if (m)
430 							*p++ = ' ';		// Replace all 0xa0 by spaces
431 						else
432 							m = *p++ = '\"';	// But the first by a '"'
433 					} else
434 						*p++ = c;
435 				}
436 				if (m)
437 					*p++ = ' ';
438 				else
439 					*p++ = '\"';			// No 0xa0, then append a space
440 
441 				if (de->type & 0x80)
442 					*p++ = ' ';
443 				else
444 					*p++ = '*';
445 
446 				*p++ = type_char_1[de->type & 0x0f];
447 				*p++ = type_char_2[de->type & 0x0f];
448 				*p++ = type_char_3[de->type & 0x0f];
449 
450 				if (de->type & 0x40)
451 					*p++ = '<';
452 				else
453 					*p++ = ' ';
454 
455 				*p++ = ' ';
456 				if (n >= 10) *p++ = ' ';
457 				if (n >= 100) *p++ = ' ';
458 				*p++ = 0;
459 			}
460 		}
461 	}
462 
463 	// Final line
464 	q = p;
465 	for (i=0; i<29; i++)
466 		*q++ = ' ';
467 
468 	n = 0;
469 	for (i=0; i<35; i++)
470 		n += bam->bitmap[i*4];
471 
472 	*p++ = 0x01;		// Dummy line link
473 	*p++ = 0x01;
474 	*p++ = n & 0xff;	// Number of free blocks as line number
475 	*p++ = (n >> 8) & 0xff;
476 
477 	*p++ = 'B';
478 	*p++ = 'L';
479 	*p++ = 'O';
480 	*p++ = 'C';
481 	*p++ = 'K';
482 	*p++ = 'S';
483 	*p++ = ' ';
484 	*p++ = 'F';
485 	*p++ = 'R';
486 	*p++ = 'E';
487 	*p++ = 'E';
488 	*p++ = '.';
489 
490 	p = q;
491 	*p++ = 0;
492 	*p++ = 0;
493 	*p++ = 0;
494 
495 	buf_len[0] = p - chan_buf[0];
496 
497 	return ST_OK;
498 }
499 
500 
501 /*
502  *  Open channel for direct buffer access
503  */
504 
open_direct(int channel,char * filename)505 uint8 D64Drive::open_direct(int channel, char *filename)
506 {
507 	int buf = -1;
508 
509 	if (filename[1] == 0)
510 		buf = alloc_buffer(-1);
511 	else
512 		if ((filename[1] >= '0') && (filename[1] <= '3') && (filename[2] == 0))
513 			buf = alloc_buffer(filename[1] - '0');
514 
515 	if (buf == -1) {
516 		set_error(ERR_NOCHANNEL);
517 		return ST_OK;
518 	}
519 
520 	// The buffers are in the 1541 RAM at $300 and are 256 bytes each
521 	chan_buf[channel] = buf_ptr[channel] = ram + 0x300 + (buf << 8);
522 	chan_mode[channel] = CHMOD_DIRECT;
523 	chan_buf_num[channel] = buf;
524 
525 	// Store actual buffer number in buffer
526 	*chan_buf[channel] = buf + '0';
527 	buf_len[channel] = 1;
528 
529 	return ST_OK;
530 }
531 
532 
533 /*
534  *  Close channel
535  */
536 
Close(int channel)537 uint8 D64Drive::Close(int channel)
538 {
539 	if (channel == 15) {
540 		close_all_channels();
541 		return ST_OK;
542 	}
543 
544 	switch (chan_mode[channel]) {
545 		case CHMOD_FREE:
546 			break;
547 
548 		case CHMOD_DIRECT:
549 			free_buffer(chan_buf_num[channel]);
550 			chan_buf[channel] = NULL;
551 			chan_mode[channel] = CHMOD_FREE;
552 			break;
553 
554 		default:
555 			delete[] chan_buf[channel];
556 			chan_buf[channel] = NULL;
557 			chan_mode[channel] = CHMOD_FREE;
558 			break;
559 	}
560 
561 	return ST_OK;
562 }
563 
564 
565 /*
566  *  Close all channels
567  */
568 
close_all_channels()569 void D64Drive::close_all_channels()
570 {
571 	for (int i=0; i<15; i++)
572 		Close(i);
573 
574 	cmd_len = 0;
575 }
576 
577 
578 /*
579  *  Read from channel
580  */
581 
Read(int channel,uint8 * byte)582 uint8 D64Drive::Read(int channel, uint8 *byte)
583 {
584 	switch (chan_mode[channel]) {
585 		case CHMOD_COMMAND:
586 			*byte = *error_ptr++;
587 			if (--error_len)
588 				return ST_OK;
589 			else {
590 				set_error(ERR_OK);
591 				return ST_EOF;
592 			}
593 			break;
594 
595 		case CHMOD_FILE:
596 			// Read next block if necessary
597 			if (chan_buf[channel][0] && !buf_len[channel]) {
598 				if (!read_sector(chan_buf[channel][0], chan_buf[channel][1], chan_buf[channel]))
599 					return ST_READ_TIMEOUT;
600 				buf_ptr[channel] = chan_buf[channel] + 2;
601 
602 				// Determine block length
603 				buf_len[channel] = chan_buf[channel][0] ? 254 : (uint8)chan_buf[channel][1]-1;
604 			}
605 
606 			if (buf_len[channel] > 0) {
607 				*byte = *buf_ptr[channel]++;
608 				if (!--buf_len[channel] && !chan_buf[channel][0])
609 					return ST_EOF;
610 				else
611 					return ST_OK;
612 			} else
613 				return ST_READ_TIMEOUT;
614 			break;
615 
616 		case CHMOD_DIRECTORY:
617 		case CHMOD_DIRECT:
618 			if (buf_len[channel] > 0) {
619 				*byte = *buf_ptr[channel]++;
620 				if (--buf_len[channel])
621 					return ST_OK;
622 				else
623 					return ST_EOF;
624 			} else
625 				return ST_READ_TIMEOUT;
626 			break;
627 	}
628 	return ST_READ_TIMEOUT;
629 }
630 
631 
632 /*
633  *  Write byte to channel
634  */
635 
Write(int channel,uint8 byte,bool eoi)636 uint8 D64Drive::Write(int channel, uint8 byte, bool eoi)
637 {
638 	switch (chan_mode[channel]) {
639 		case CHMOD_FREE:
640 			set_error(ERR_FILENOTOPEN);
641 			break;
642 
643 		case CHMOD_COMMAND:
644 			// Collect characters and execute command on EOI
645 			if (cmd_len >= 40)
646 				return ST_TIMEOUT;
647 
648 			cmd_buffer[cmd_len++] = byte;
649 
650 			if (eoi) {
651 				cmd_buffer[cmd_len++] = 0;
652 				cmd_len = 0;
653 				execute_command(cmd_buffer);
654 			}
655 			return ST_OK;
656 
657 		case CHMOD_DIRECTORY:
658 			set_error(ERR_WRITEFILEOPEN);
659 			break;
660 	}
661 	return ST_TIMEOUT;
662 }
663 
664 
665 /*
666  *  Execute command string
667  */
668 
execute_command(char * command)669 void D64Drive::execute_command(char *command)
670 {
671 	uint16 adr;
672 	int len;
673 
674 	switch (command[0]) {
675 		case 'B':
676 			if (command[1] != '-')
677 				set_error(ERR_SYNTAX30);
678 			else
679 				switch (command[2]) {
680 					case 'R':
681 						block_read_cmd(&command[3]);
682 						break;
683 
684 					case 'P':
685 						buffer_ptr_cmd(&command[3]);
686 						break;
687 
688 					case 'A':
689 					case 'F':
690 					case 'W':
691 						set_error(ERR_WRITEPROTECT);
692 						break;
693 
694 					default:
695 						set_error(ERR_SYNTAX30);
696 						break;
697 				}
698 			break;
699 
700 		case 'M':
701 			if (command[1] != '-')
702 				set_error(ERR_SYNTAX30);
703 			else
704 				switch (command[2]) {
705 					case 'R':
706 						adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
707 						error_ptr = (char *)(ram + (adr & 0x07ff));
708 						if (!(error_len = (uint8)command[5]))
709 							error_len = 1;
710 						break;
711 
712 					case 'W':
713 						adr = ((uint8)command[4] << 8) | ((uint8)command[3]);
714 						len = (uint8)command[5];
715 						for (int i=0; i<len; i++)
716 							ram[adr+i] = (uint8)command[i+6];
717 						break;
718 
719 					default:
720 						set_error(ERR_SYNTAX30);
721 				}
722 			break;
723 
724 		case 'I':
725 			close_all_channels();
726 			read_sector(18, 0, (uint8 *)bam);
727 			set_error(ERR_OK);
728 			break;
729 
730 		case 'U':
731 			switch (command[1] & 0x0f) {
732 				case 1:		// U1/UA: Block-Read
733 					block_read_cmd(&command[2]);
734 					break;
735 
736 				case 2:		// U2/UB: Block-Write
737 					set_error(ERR_WRITEPROTECT);
738 					break;
739 
740 				case 10:	// U:/UJ: Reset
741 					Reset();
742 					break;
743 
744 				default:
745 					set_error(ERR_SYNTAX30);
746 					break;
747 			}
748 			break;
749 
750 		case 'G':
751 			if (command[1] != ':')
752 				set_error(ERR_SYNTAX30);
753 			else
754 				chd64_cmd(&command[2]);
755 			break;
756 
757 		case 'C':
758 		case 'N':
759 		case 'R':
760 		case 'S':
761 		case 'V':
762 			set_error(ERR_WRITEPROTECT);
763 			break;
764 
765 		default:
766 			set_error(ERR_SYNTAX30);
767 			break;
768 	}
769 }
770 
771 
772 /*
773  *  Execute B-R command
774  */
775 
block_read_cmd(char * command)776 void D64Drive::block_read_cmd(char *command)
777 {
778 	int channel, drvnum, track, sector;
779 
780 	if (parse_bcmd(command, &channel, &drvnum, &track, &sector)) {
781 		if (chan_mode[channel] == CHMOD_DIRECT) {
782 			read_sector(track, sector, buf_ptr[channel] = chan_buf[channel]);
783 			buf_len[channel] = 256;
784 			set_error(ERR_OK);
785 		} else
786 			set_error(ERR_NOCHANNEL);
787 	} else
788 		set_error(ERR_SYNTAX30);
789 }
790 
791 
792 /*
793  *  Execute B-P command
794  */
795 
buffer_ptr_cmd(char * command)796 void D64Drive::buffer_ptr_cmd(char *command)
797 {
798 	int channel, pointer, i;
799 
800 	if (parse_bcmd(command, &channel, &pointer, &i, &i)) {
801 		if (chan_mode[channel] == CHMOD_DIRECT) {
802 			buf_ptr[channel] = chan_buf[channel] + pointer;
803 			buf_len[channel] = 256 - pointer;
804 			set_error(ERR_OK);
805 		} else
806 			set_error(ERR_NOCHANNEL);
807 	} else
808 		set_error(ERR_SYNTAX30);
809 }
810 
811 
812 /*
813  *  Parse block command parameters
814  *  true: OK, false: error
815  */
816 
parse_bcmd(char * cmd,int * arg1,int * arg2,int * arg3,int * arg4)817 bool D64Drive::parse_bcmd(char *cmd, int *arg1, int *arg2, int *arg3, int *arg4)
818 {
819 	int i;
820 
821 	if (*cmd == ':') cmd++;
822 
823 	// Read four parameters separated by space, cursor right or comma
824 	while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
825 	if (!*cmd) return false;
826 
827 	i = 0;
828 	while (*cmd >= 0x30 && *cmd < 0x40) {
829 		i *= 10;
830 		i += *cmd++ & 0x0f;
831 	}
832 	*arg1 = i & 0xff;
833 
834 	while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
835 	if (!*cmd) return false;
836 
837 	i = 0;
838 	while (*cmd >= 0x30 && *cmd < 0x40) {
839 		i *= 10;
840 		i += *cmd++ & 0x0f;
841 	}
842 	*arg2 = i & 0xff;
843 
844 	while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
845 	if (!*cmd) return false;
846 
847 	i = 0;
848 	while (*cmd >= 0x30 && *cmd < 0x40) {
849 		i *= 10;
850 		i += *cmd++ & 0x0f;
851 	}
852 	*arg3 = i & 0xff;
853 
854 	while (*cmd == ' ' || *cmd == 0x1d || *cmd == 0x2c) cmd++;
855 	if (!*cmd) return false;
856 
857 	i = 0;
858 	while (*cmd >= 0x30 && *cmd < 0x40) {
859 		i *= 10;
860 		i += *cmd++ & 0x0f;
861 	}
862 	*arg4 = i & 0xff;
863 
864 	return true;
865 }
866 
867 
868 /*
869  *  Execute 'G' command
870  */
871 
chd64_cmd(char * d64name)872 void D64Drive::chd64_cmd(char *d64name)
873 {
874 	char str[NAMEBUF_LENGTH];
875 	char *p = str;
876 
877 	// Convert .d64 file name
878 	for (int i=0; i<NAMEBUF_LENGTH && (*p++ = conv_from_64(*d64name++, false)); i++) ;
879 
880 	close_all_channels();
881 
882 	// G:. resets the .d64 file name to its original setting
883 	if (str[0] == '.' && str[1] == 0)
884 		open_close_d64_file(orig_d64_name);
885 	else
886 		open_close_d64_file(str);
887 
888 	// Read BAM
889 	read_sector(18, 0, (uint8 *)bam);
890 }
891 
892 
893 /*
894  *  Reset drive
895  */
896 
Reset(void)897 void D64Drive::Reset(void)
898 {
899 	close_all_channels();
900 
901 	read_sector(18, 0, (uint8 *)bam);
902 
903 	cmd_len = 0;
904 	for (int i=0; i<4; i++)
905 		buf_free[i] = true;
906 
907 	set_error(ERR_STARTUP);
908 }
909 
910 
911 /*
912  *  Allocate floppy buffer
913  *   -> Desired buffer number or -1
914  *   <- Allocated buffer number or -1
915  */
916 
alloc_buffer(int want)917 int D64Drive::alloc_buffer(int want)
918 {
919 	if (want == -1) {
920 		for (want=3; want>=0; want--)
921 			if (buf_free[want]) {
922 				buf_free[want] = false;
923 				return want;
924 			}
925 		return -1;
926 	}
927 
928 	if (want < 4)
929 		if (buf_free[want]) {
930 			buf_free[want] = false;
931 			return want;
932 		} else
933 			return -1;
934 	else
935 		return -1;
936 }
937 
938 
939 /*
940  *  Free floppy buffer
941  */
942 
free_buffer(int buf)943 void D64Drive::free_buffer(int buf)
944 {
945 	buf_free[buf] = true;
946 }
947 
948 
949 /*
950  *  Read sector (256 bytes)
951  *  true: success, false: error
952  */
953 
read_sector(int track,int sector,uint8 * buffer)954 bool D64Drive::read_sector(int track, int sector, uint8 *buffer)
955 {
956 	int offset;
957 
958 	// Convert track/sector to byte offset in file
959 	if ((offset = offset_from_ts(track, sector)) < 0) {
960 		set_error(ERR_ILLEGALTS);
961 		return false;
962 	}
963 
964 	if (the_file == NULL) {
965 		set_error(ERR_NOTREADY);
966 		return false;
967 	}
968 
969 #ifdef AMIGA
970 	if (offset != ftell(the_file))
971 		fseek(the_file, offset + image_header, SEEK_SET);
972 #else
973 	fseek(the_file, offset + image_header, SEEK_SET);
974 #endif
975 	fread(buffer, 256, 1, the_file);
976 	return true;
977 }
978 
979 
980 /*
981  *  Convert track/sector to offset
982  */
983 
984 const int num_sectors[41] = {
985 	0,
986 	21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,21,
987 	19,19,19,19,19,19,19,
988 	18,18,18,18,18,18,
989 	17,17,17,17,17,
990 	17,17,17,17,17		// Tracks 36..40
991 };
992 
993 const int sector_offset[41] = {
994 	0,
995 	0,21,42,63,84,105,126,147,168,189,210,231,252,273,294,315,336,
996 	357,376,395,414,433,452,471,
997 	490,508,526,544,562,580,
998 	598,615,632,649,666,
999 	683,700,717,734,751	// Tracks 36..40
1000 };
1001 
offset_from_ts(int track,int sector)1002 int D64Drive::offset_from_ts(int track, int sector)
1003 {
1004 	if ((track < 1) || (track > NUM_TRACKS) ||
1005 		(sector < 0) || (sector >= num_sectors[track]))
1006 		return -1;
1007 
1008 	return (sector_offset[track] + sector) << 8;
1009 }
1010 
1011 
1012 /*
1013  *  Conversion PETSCII->ASCII
1014  */
1015 
conv_from_64(uint8 c,bool map_slash)1016 uint8 D64Drive::conv_from_64(uint8 c, bool map_slash)
1017 {
1018 	if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
1019 		return c ^ 0x20;
1020 	if ((c >= 0xc1) && (c <= 0xda))
1021 		return c ^ 0x80;
1022 	if ((c == '/') && map_slash && ThePrefs.MapSlash)
1023 		return '\\';
1024 	return c;
1025 }
1026