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, 2 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

# User Rev Content
1 cebix 1.1 /*
2     * IEC.cpp - IEC bus routines, 1541 emulation (DOS level)
3     *
4 cebix 1.6 * Frodo (C) 1994-1997,2002-2004 Christian Bauer
5 cebix 1.1 *
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 cebix 1.5 drive[i] = new ImageDrive(this, ThePrefs.DrivePath[i]);
72 cebix 1.1 else
73 cebix 1.7 drive[i] = new ArchDrive(this, ThePrefs.DrivePath[i]);
74 cebix 1.1 }
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 cebix 1.5 drive[i] = new ImageDrive(this, prefs->DrivePath[i]);
124 cebix 1.1 else
125 cebix 1.7 drive[i] = new ArchDrive(this, prefs->DrivePath[i]);
126 cebix 1.1 }
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 cebix 1.4 uint8 IEC::In(uint8 &byte)
214 cebix 1.1 {
215     if (talker_active && (received_cmd == CMD_DATA))
216     return data_in(byte);
217    
218 cebix 1.4 byte = 0;
219 cebix 1.1 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 cebix 1.3 return listener->Open(sec_addr, name_buf, name_len);
371 cebix 1.1 }
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 cebix 1.4 uint8 IEC::data_in(uint8 &byte)
392 cebix 1.1 {
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 cebix 1.3 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 cebix 1.1 };
447    
448 cebix 1.3 void Drive::set_error(int error, int track, int sector)
449 cebix 1.1 {
450 cebix 1.3 // 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 cebix 1.4 current_error = error;
455 cebix 1.1
456     // Set drive condition
457 cebix 1.3 if (error != ERR_OK && error != ERR_SCRATCHED)
458 cebix 1.1 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 cebix 1.3 }
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 cebix 1.7 uint8 ascii2petscii(char c)
852 cebix 1.3 {
853     if ((c >= 'A') && (c <= 'Z') || (c >= 'a') && (c <= 'z'))
854     return c ^ 0x20;
855     return c;
856     }
857    
858 cebix 1.7 void ascii2petscii(uint8 *dest, const char *src, int n)
859 cebix 1.3 {
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 cebix 1.7 void petscii2ascii(char *dest, const uint8 *src, int n)
873 cebix 1.3 {
874     while (n-- && (*dest++ = petscii2ascii(*src++))) ;
875 cebix 1.1 }