ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/IEC.cpp
Revision: 1.7
Committed: 2004-01-14T16:54:46Z (20 years, 3 months ago) by cebix
Branch: MAIN
Changes since 1.6: +5 -5 lines
Log Message:
- backported T64 code from Frodo V5, handles .P00 files
- T64Drive renamed to ArchDrive
- PETSCII characters are uint8s, not chars

File Contents

# Content
1 /*
2 * IEC.cpp - IEC bus routines, 1541 emulation (DOS level)
3 *
4 * Frodo (C) 1994-1997,2002-2004 Christian Bauer
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program 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
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19 */
20
21 /*
22 * Notes:
23 * ------
24 *
25 * - There are three kinds of devices on the IEC bus: controllers,
26 * listeners and talkers. We are always the controller and we
27 * can additionally be either listener or talker. There can be
28 * only one listener and one talker active at the same time (the
29 * real IEC bus allows multiple listeners, but we don't).
30 * - There is one Drive object for every emulated drive (8..11).
31 * A pointer to one of them is stored in "listener"/"talker"
32 * when talk()/listen() is called and is used by the functions
33 * called afterwards.
34 * - The Drive objects have four virtual functions so that the
35 * interface to them is independent of their implementation:
36 * Open() opens a channel
37 * Close() closes a channel
38 * Read() reads from a channel
39 * Write() writes to a channel
40 * - The EOI/EOF signal is special on the IEC bus in that it is
41 * Sent before the last byte, not after it.
42 */
43
44 #include "sysdeps.h"
45
46 #include "IEC.h"
47 #include "1541fs.h"
48 #include "1541d64.h"
49 #include "1541t64.h"
50 #include "Prefs.h"
51 #include "Display.h"
52
53
54 /*
55 * Constructor: Initialize variables
56 */
57
58 IEC::IEC(C64Display *display) : the_display(display)
59 {
60 int i;
61
62 // Create drives 8..11
63 for (i=0; i<4; i++)
64 drive[i] = NULL; // Important because UpdateLEDs is called from the drive constructors (via set_error)
65
66 if (!ThePrefs.Emul1541Proc)
67 for (i=0; i<4; i++) {
68 if (ThePrefs.DriveType[i] == DRVTYPE_DIR)
69 drive[i] = new FSDrive(this, ThePrefs.DrivePath[i]);
70 else if (ThePrefs.DriveType[i] == DRVTYPE_D64)
71 drive[i] = new ImageDrive(this, ThePrefs.DrivePath[i]);
72 else
73 drive[i] = new ArchDrive(this, ThePrefs.DrivePath[i]);
74 }
75
76 listener_active = talker_active = false;
77 listening = false;
78 }
79
80
81 /*
82 * Destructor: Delete drives
83 */
84
85 IEC::~IEC()
86 {
87 for (int i=0; i<4; i++)
88 delete drive[i];
89 }
90
91
92 /*
93 * Reset all drives
94 */
95
96 void IEC::Reset(void)
97 {
98 for (int i=0; i<4; i++)
99 if (drive[i] != NULL && drive[i]->Ready)
100 drive[i]->Reset();
101
102 UpdateLEDs();
103 }
104
105
106 /*
107 * Preferences have changed, prefs points to new preferences,
108 * ThePrefs still holds the previous ones. Check if drive settings
109 * have changed.
110 */
111
112 void IEC::NewPrefs(Prefs *prefs)
113 {
114 // Delete and recreate all changed drives
115 for (int i=0; i<4; i++)
116 if ((ThePrefs.DriveType[i] != prefs->DriveType[i]) || strcmp(ThePrefs.DrivePath[i], prefs->DrivePath[i]) || ThePrefs.Emul1541Proc != prefs->Emul1541Proc) {
117 delete drive[i];
118 drive[i] = NULL; // Important because UpdateLEDs is called from drive constructors (via set_error())
119 if (!prefs->Emul1541Proc) {
120 if (prefs->DriveType[i] == DRVTYPE_DIR)
121 drive[i] = new FSDrive(this, prefs->DrivePath[i]);
122 else if (prefs->DriveType[i] == DRVTYPE_D64)
123 drive[i] = new ImageDrive(this, prefs->DrivePath[i]);
124 else
125 drive[i] = new ArchDrive(this, prefs->DrivePath[i]);
126 }
127 }
128
129 UpdateLEDs();
130 }
131
132
133 /*
134 * Update drive LED display
135 */
136
137 void IEC::UpdateLEDs(void)
138 {
139 if (drive[0] != NULL && drive[1] != NULL && drive[2] != NULL && drive[3] != NULL)
140 the_display->UpdateLEDs(drive[0]->LED, drive[1]->LED, drive[2]->LED, drive[3]->LED);
141 }
142
143
144 /*
145 * Output one byte
146 */
147
148 uint8 IEC::Out(uint8 byte, bool eoi)
149 {
150 if (listener_active) {
151 if (received_cmd == CMD_OPEN)
152 return open_out(byte, eoi);
153 if (received_cmd == CMD_DATA)
154 return data_out(byte, eoi);
155 return ST_TIMEOUT;
156 } else
157 return ST_TIMEOUT;
158 }
159
160
161 /*
162 * Output one byte with ATN (Talk/Listen/Untalk/Unlisten)
163 */
164
165 uint8 IEC::OutATN(uint8 byte)
166 {
167 received_cmd = sec_addr = 0; // Command is sent with secondary address
168 switch (byte & 0xf0) {
169 case ATN_LISTEN:
170 listening = true;
171 return listen(byte & 0x0f);
172 case ATN_UNLISTEN:
173 listening = false;
174 return unlisten();
175 case ATN_TALK:
176 listening = false;
177 return talk(byte & 0x0f);
178 case ATN_UNTALK:
179 listening = false;
180 return untalk();
181 }
182 return ST_TIMEOUT;
183 }
184
185
186 /*
187 * Output secondary address
188 */
189
190 uint8 IEC::OutSec(uint8 byte)
191 {
192 if (listening) {
193 if (listener_active) {
194 sec_addr = byte & 0x0f;
195 received_cmd = byte & 0xf0;
196 return sec_listen();
197 }
198 } else {
199 if (talker_active) {
200 sec_addr = byte & 0x0f;
201 received_cmd = byte & 0xf0;
202 return sec_talk();
203 }
204 }
205 return ST_TIMEOUT;
206 }
207
208
209 /*
210 * Read one byte
211 */
212
213 uint8 IEC::In(uint8 &byte)
214 {
215 if (talker_active && (received_cmd == CMD_DATA))
216 return data_in(byte);
217
218 byte = 0;
219 return ST_TIMEOUT;
220 }
221
222
223 /*
224 * Assert ATN (for Untalk)
225 */
226
227 void IEC::SetATN(void)
228 {
229 // Only needed for real IEC
230 }
231
232
233 /*
234 * Release ATN
235 */
236
237 void IEC::RelATN(void)
238 {
239 // Only needed for real IEC
240 }
241
242
243 /*
244 * Talk-attention turn-around
245 */
246
247 void IEC::Turnaround(void)
248 {
249 // Only needed for real IEC
250 }
251
252
253 /*
254 * System line release
255 */
256
257 void IEC::Release(void)
258 {
259 // Only needed for real IEC
260 }
261
262
263 /*
264 * Listen
265 */
266
267 uint8 IEC::listen(int device)
268 {
269 if ((device >= 8) && (device <= 11)) {
270 if ((listener = drive[device-8]) != NULL && listener->Ready) {
271 listener_active = true;
272 return ST_OK;
273 }
274 }
275
276 listener_active = false;
277 return ST_NOTPRESENT;
278 }
279
280
281 /*
282 * Talk
283 */
284
285 uint8 IEC::talk(int device)
286 {
287 if ((device >= 8) && (device <= 11)) {
288 if ((talker = drive[device-8]) != NULL && talker->Ready) {
289 talker_active = true;
290 return ST_OK;
291 }
292 }
293
294 talker_active = false;
295 return ST_NOTPRESENT;
296 }
297
298
299 /*
300 * Unlisten
301 */
302
303 uint8 IEC::unlisten(void)
304 {
305 listener_active = false;
306 return ST_OK;
307 }
308
309
310 /*
311 * Untalk
312 */
313
314 uint8 IEC::untalk(void)
315 {
316 talker_active = false;
317 return ST_OK;
318 }
319
320
321 /*
322 * Secondary address after Listen
323 */
324
325 uint8 IEC::sec_listen(void)
326 {
327 switch (received_cmd) {
328
329 case CMD_OPEN: // Prepare for receiving the file name
330 name_ptr = name_buf;
331 name_len = 0;
332 return ST_OK;
333
334 case CMD_CLOSE: // Close channel
335 if (listener->LED != DRVLED_ERROR) {
336 listener->LED = DRVLED_OFF; // Turn off drive LED
337 UpdateLEDs();
338 }
339 return listener->Close(sec_addr);
340 }
341 return ST_OK;
342 }
343
344
345 /*
346 * Secondary address after Talk
347 */
348
349 uint8 IEC::sec_talk(void)
350 {
351 return ST_OK;
352 }
353
354
355 /*
356 * Byte after Open command: Store character in file name, open file on EOI
357 */
358
359 uint8 IEC::open_out(uint8 byte, bool eoi)
360 {
361 if (name_len < NAMEBUF_LENGTH) {
362 *name_ptr++ = byte;
363 name_len++;
364 }
365
366 if (eoi) {
367 *name_ptr = 0; // End string
368 listener->LED = DRVLED_ON; // Turn on drive LED
369 UpdateLEDs();
370 return listener->Open(sec_addr, name_buf, name_len);
371 }
372
373 return ST_OK;
374 }
375
376
377 /*
378 * Write byte to channel
379 */
380
381 uint8 IEC::data_out(uint8 byte, bool eoi)
382 {
383 return listener->Write(sec_addr, byte, eoi);
384 }
385
386
387 /*
388 * Read byte from channel
389 */
390
391 uint8 IEC::data_in(uint8 &byte)
392 {
393 return talker->Read(sec_addr, byte);
394 }
395
396
397 /*
398 * Drive constructor
399 */
400
401 Drive::Drive(IEC *iec)
402 {
403 the_iec = iec;
404 LED = DRVLED_OFF;
405 Ready = false;
406 set_error(ERR_STARTUP);
407 }
408
409
410 /*
411 * Set error message on drive
412 */
413
414 // 1541 error messages
415 static const char *Errors_1541[] = {
416 "00, OK,%02d,%02d\x0d",
417 "01, FILES SCRATCHED,%02d,%02d\x0d",
418 "03, UNIMPLEMENTED,%02d,%02d\x0d",
419 "20, READ ERROR,%02d,%02d\x0d",
420 "21, READ ERROR,%02d,%02d\x0d",
421 "22, READ ERROR,%02d,%02d\x0d",
422 "23, READ ERROR,%02d,%02d\x0d",
423 "24, READ ERROR,%02d,%02d\x0d",
424 "25, WRITE ERROR,%02d,%02d\x0d",
425 "26, WRITE PROTECT ON,%02d,%02d\x0d",
426 "27, READ ERROR,%02d,%02d\x0d",
427 "28, WRITE ERROR,%02d,%02d\x0d",
428 "29, DISK ID MISMATCH,%02d,%02d\x0d",
429 "30, SYNTAX ERROR,%02d,%02d\x0d",
430 "31, SYNTAX ERROR,%02d,%02d\x0d",
431 "32, SYNTAX ERROR,%02d,%02d\x0d",
432 "33, SYNTAX ERROR,%02d,%02d\x0d",
433 "34, SYNTAX ERROR,%02d,%02d\x0d",
434 "60, WRITE FILE OPEN,%02d,%02d\x0d",
435 "61, FILE NOT OPEN,%02d,%02d\x0d",
436 "62, FILE NOT FOUND,%02d,%02d\x0d",
437 "63, FILE EXISTS,%02d,%02d\x0d",
438 "64, FILE TYPE MISMATCH,%02d,%02d\x0d",
439 "65, NO BLOCK,%02d,%02d\x0d",
440 "66, ILLEGAL TRACK OR SECTOR,%02d,%02d\x0d",
441 "70, NO CHANNEL,%02d,%02d\x0d",
442 "71, DIR ERROR,%02d,%02d\x0d",
443 "72, DISK FULL,%02d,%02d\x0d",
444 "73, CBM DOS V2.6 1541,%02d,%02d\x0d",
445 "74, DRIVE NOT READY,%02d,%02d\x0d"
446 };
447
448 void Drive::set_error(int error, int track, int sector)
449 {
450 // Write error message to buffer
451 sprintf(error_buf, Errors_1541[error], track, sector);
452 error_ptr = error_buf;
453 error_len = strlen(error_buf);
454 current_error = error;
455
456 // Set drive condition
457 if (error != ERR_OK && error != ERR_SCRATCHED)
458 if (error == ERR_STARTUP)
459 LED = DRVLED_OFF;
460 else
461 LED = DRVLED_ERROR;
462 else if (LED == DRVLED_ERROR)
463 LED = DRVLED_OFF;
464 the_iec->UpdateLEDs();
465 }
466
467
468 /*
469 * Parse file name, determine access mode and file type
470 */
471
472 void Drive::parse_file_name(const uint8 *src, int src_len, uint8 *dest, int &dest_len, int &mode, int &type, int &rec_len, bool convert_charset)
473 {
474 // If the string contains a ':', the file name starts after that
475 const uint8 *p = (const uint8 *)memchr(src, ':', src_len);
476 if (p) {
477 p++;
478 src_len -= p - src;
479 } else
480 p = src;
481
482 // Transfer file name upto ','
483 dest_len = 0;
484 uint8 *q = dest;
485 while (*p != ',' && src_len-- > 0) {
486 if (convert_charset)
487 *q++ = petscii2ascii(*p++);
488 else
489 *q++ = *p++;
490 dest_len++;
491 }
492 *q++ = 0;
493
494 // Strip trailing CRs
495 while (dest_len > 0 && dest[dest_len - 1] == 0x0d)
496 dest[--dest_len] = 0;
497
498 // Look for mode and type parameters separated by ','
499 p++; src_len--;
500 while (src_len > 0) {
501 switch (*p) {
502 case 'D':
503 type = FTYPE_DEL;
504 break;
505 case 'S':
506 type = FTYPE_SEQ;
507 break;
508 case 'P':
509 type = FTYPE_PRG;
510 break;
511 case 'U':
512 type = FTYPE_USR;
513 break;
514 case 'L':
515 type = FTYPE_REL;
516 while (*p != ',' && src_len-- > 0) p++;
517 p++; src_len--;
518 rec_len = *p++; src_len--;
519 if (src_len < 0)
520 rec_len = 0;
521 break;
522 case 'R':
523 mode = FMODE_READ;
524 break;
525 case 'W':
526 mode = FMODE_WRITE;
527 break;
528 case 'A':
529 mode = FMODE_APPEND;
530 break;
531 case 'M':
532 mode = FMODE_M;
533 break;
534 }
535
536 // Skip to ','
537 while (*p != ',' && src_len-- > 0) p++;
538 p++; src_len--;
539 }
540 }
541
542
543 /*
544 * Execute DOS command (parse command and call appropriate routine)
545 */
546
547 static void parse_block_cmd_args(const uint8 *p, int &arg1, int &arg2, int &arg3, int &arg4)
548 {
549 arg1 = arg2 = arg3 = arg4 = 0;
550
551 while (*p == ' ' || *p == 0x1d || *p == ',') p++;
552 while (*p >= '0' && *p < '@')
553 arg1 = arg1 * 10 + (*p++ & 0x0f);
554
555 while (*p == ' ' || *p == 0x1d || *p == ',') p++;
556 while (*p >= '0' && *p < '@')
557 arg2 = arg2 * 10 + (*p++ & 0x0f);
558
559 while (*p == ' ' || *p == 0x1d || *p == ',') p++;
560 while (*p >= '0' && *p < '@')
561 arg3 = arg3 * 10 + (*p++ & 0x0f);
562
563 while (*p == ' ' || *p == 0x1d || *p == ',') p++;
564 while (*p >= '0' && *p < '@')
565 arg4 = arg4 * 10 + (*p++ & 0x0f);
566 }
567
568 void Drive::execute_cmd(const uint8 *cmd, int cmd_len)
569 {
570 // Strip trailing CRs
571 while (cmd_len > 0 && cmd[cmd_len - 1] == 0x0d)
572 cmd_len--;
573
574 // Find token delimiters
575 const uint8 *colon = (const uint8 *)memchr(cmd, ':', cmd_len);
576 const uint8 *equal = colon ? (const uint8 *)memchr(colon, '=', cmd_len - (colon - cmd)) : NULL;
577 const uint8 *comma = (const uint8 *)memchr(cmd, ',', cmd_len);
578 const uint8 *minus = (const uint8 *)memchr(cmd, '-', cmd_len);
579
580 // Parse command name
581 set_error(ERR_OK);
582 switch (cmd[0]) {
583 case 'B': // Block/buffer
584 if (!minus)
585 set_error(ERR_SYNTAX31);
586 else {
587 // Parse arguments (up to 4 decimal numbers separated by
588 // space, cursor right or comma)
589 const uint8 *p = colon ? colon + 1 : cmd + 3;
590 int arg1, arg2, arg3, arg4;
591 parse_block_cmd_args(p, arg1, arg2, arg3, arg4);
592
593 // Switch on command
594 switch (minus[1]) {
595 case 'R':
596 block_read_cmd(arg1, arg3, arg4);
597 break;
598 case 'W':
599 block_write_cmd(arg1, arg3, arg4);
600 break;
601 case 'E':
602 block_execute_cmd(arg1, arg3, arg4);
603 break;
604 case 'A':
605 block_allocate_cmd(arg2, arg3);
606 break;
607 case 'F':
608 block_free_cmd(arg2, arg3);
609 break;
610 case 'P':
611 buffer_pointer_cmd(arg1, arg2);
612 break;
613 default:
614 set_error(ERR_SYNTAX31);
615 break;
616 }
617 }
618 break;
619
620 case 'M': // Memory
621 if (cmd[1] != '-')
622 set_error(ERR_SYNTAX31);
623 else {
624 // Read parameters
625 uint16 adr = uint8(cmd[3]) | (uint8(cmd[4]) << 8);
626 uint8 len = uint8(cmd[5]);
627
628 // Switch on command
629 switch (cmd[2]) {
630 case 'R':
631 mem_read_cmd(adr, (cmd_len < 6) ? 1 : len);
632 break;
633 case 'W':
634 mem_write_cmd(adr, len, (uint8 *)cmd + 6);
635 break;
636 case 'E':
637 mem_execute_cmd(adr);
638 break;
639 default:
640 set_error(ERR_SYNTAX31);
641 break;
642 }
643 }
644 break;
645
646 case 'C': // Copy
647 if (!colon)
648 set_error(ERR_SYNTAX31);
649 else if (!equal || memchr(cmd, '*', cmd_len) || memchr(cmd, '?', cmd_len) || (comma && comma < equal))
650 set_error(ERR_SYNTAX30);
651 else
652 copy_cmd(colon + 1, equal - colon - 1, equal + 1, cmd_len - (equal + 1 - cmd));
653 break;
654
655 case 'R': // Rename
656 if (!colon)
657 set_error(ERR_SYNTAX34);
658 else if (!equal || comma || memchr(cmd, '*', cmd_len) || memchr(cmd, '?', cmd_len))
659 set_error(ERR_SYNTAX30);
660 else
661 rename_cmd(colon + 1, equal - colon - 1, equal + 1, cmd_len - (equal + 1 - cmd));
662 break;
663
664 case 'S': // Scratch
665 if (!colon)
666 set_error(ERR_SYNTAX34);
667 else
668 scratch_cmd(colon + 1, cmd_len - (colon + 1 - cmd));
669 break;
670
671 case 'P': // Position
672 position_cmd(cmd + 1, cmd_len - 1);
673 break;
674
675 case 'I': // Initialize
676 initialize_cmd();
677 break;
678
679 case 'N': // New (format)
680 if (!colon)
681 set_error(ERR_SYNTAX34);
682 else
683 new_cmd(colon + 1, comma ? (comma - colon - 1) : cmd_len - (colon + 1 - cmd), comma);
684 break;
685
686 case 'V': // Validate
687 validate_cmd();
688 break;
689
690 case 'U': // User
691 if (cmd[1] == '0')
692 break;
693 switch (cmd[1] & 0x0f) {
694 case 1: { // U1/UA: Read block
695 const uint8 *p = colon ? colon + 1 : cmd + 2;
696 int arg1, arg2, arg3, arg4;
697 parse_block_cmd_args(p, arg1, arg2, arg3, arg4);
698 block_read_cmd(arg1, arg3, arg4, true);
699 break;
700 }
701 case 2: { // U2/UB: Write block
702 const uint8 *p = colon ? colon + 1 : cmd + 2;
703 int arg1, arg2, arg3, arg4;
704 parse_block_cmd_args(p, arg1, arg2, arg3, arg4);
705 block_write_cmd(arg1, arg3, arg4, true);
706 break;
707 }
708 case 9: // U9/UI: C64/VC20 mode switch
709 if (cmd[2] != '+' && cmd[2] != '-')
710 Reset();
711 break;
712 case 10: // U:/UJ: Reset
713 Reset();
714 break;
715 default:
716 set_error(ERR_UNIMPLEMENTED);
717 break;
718 }
719 break;
720
721 default:
722 set_error(ERR_SYNTAX31);
723 break;
724 }
725 }
726
727 // BLOCK-READ:channel,0,track,sector
728 void Drive::block_read_cmd(int channel, int track, int sector, bool user_cmd)
729 {
730 set_error(ERR_UNIMPLEMENTED);
731 }
732
733 // BLOCK-WRITE:channel,0,track,sector
734 void Drive::block_write_cmd(int channel, int track, int sector, bool user_cmd)
735 {
736 set_error(ERR_UNIMPLEMENTED);
737 }
738
739 // BLOCK-EXECUTE:channel,0,track,sector
740 void Drive::block_execute_cmd(int channel, int track, int sector)
741 {
742 set_error(ERR_UNIMPLEMENTED);
743 }
744
745 // BLOCK-ALLOCATE:0,track,sector
746 void Drive::block_allocate_cmd(int track, int sector)
747 {
748 set_error(ERR_UNIMPLEMENTED);
749 }
750
751 // BLOCK-FREE:0,track,sector
752 void Drive::block_free_cmd(int track, int sector)
753 {
754 set_error(ERR_UNIMPLEMENTED);
755 }
756
757 // BUFFER-POINTER:channel,pos
758 void Drive::buffer_pointer_cmd(int channel, int pos)
759 {
760 set_error(ERR_UNIMPLEMENTED);
761 }
762
763 // M-R<adr low><adr high>[<number>]
764 void Drive::mem_read_cmd(uint16 adr, uint8 len)
765 {
766 unsupp_cmd();
767 error_ptr = error_buf;
768 error_buf[0] = 0;
769 error_len = 0;
770 set_error(ERR_OK);
771 }
772
773 // M-W<adr low><adr high><number><data...>
774 void Drive::mem_write_cmd(uint16 adr, uint8 len, uint8 *p)
775 {
776 set_error(ERR_UNIMPLEMENTED);
777 }
778
779 // M-E<adr low><adr high>
780 void Drive::mem_execute_cmd(uint16 adr)
781 {
782 set_error(ERR_UNIMPLEMENTED);
783 }
784
785 // COPY:new=file1,file2,...
786 // ^ ^
787 // new_file old_files
788 void Drive::copy_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_files, int old_files_len)
789 {
790 set_error(ERR_UNIMPLEMENTED);
791 }
792
793 // RENAME:new=old
794 // ^ ^
795 // new_file old_file
796 void Drive::rename_cmd(const uint8 *new_file, int new_file_len, const uint8 *old_file, int old_file_len)
797 {
798 set_error(ERR_UNIMPLEMENTED);
799 }
800
801 // SCRATCH:file1,file2,...
802 // ^
803 // files
804 void Drive::scratch_cmd(const uint8 *files, int files_len)
805 {
806 set_error(ERR_UNIMPLEMENTED);
807 }
808
809 // P<channel><record low><record high><byte>
810 // ^
811 // cmd
812 void Drive::position_cmd(const uint8 *cmd, int cmd_len)
813 {
814 set_error(ERR_UNIMPLEMENTED);
815 }
816
817 // INITIALIZE
818 void Drive::initialize_cmd(void)
819 {
820 set_error(ERR_UNIMPLEMENTED);
821 }
822
823 // NEW:name,id
824 // ^ ^
825 // name comma (or NULL)
826 void Drive::new_cmd(const uint8 *name, int name_len, const uint8 *comma)
827 {
828 set_error(ERR_UNIMPLEMENTED);
829 }
830
831 // VALIDATE
832 void Drive::validate_cmd(void)
833 {
834 set_error(ERR_UNIMPLEMENTED);
835 }
836
837
838 /*
839 * Notice user of unsupported drive command
840 */
841
842 void Drive::unsupp_cmd(void)
843 {
844 }
845
846
847 /*
848 * Convert PETSCII<->ASCII
849 */
850
851 uint8 ascii2petscii(char c)
852 {
853 if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
854 return c ^ 0x20;
855 return c;
856 }
857
858 void ascii2petscii(uint8 *dest, const char *src, int n)
859 {
860 while (n-- && (*dest++ = ascii2petscii(*src++))) ;
861 }
862
863 char petscii2ascii(uint8 c)
864 {
865 if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
866 return c ^ 0x20;
867 if ((c >= 0xc1) && (c <= 0xda))
868 return c ^ 0x80;
869 return c;
870 }
871
872 void petscii2ascii(char *dest, const uint8 *src, int n)
873 {
874 while (n-- && (*dest++ = petscii2ascii(*src++))) ;
875 }