ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/IEC.cpp
Revision: 1.9
Committed: 2005-06-27T19:55:48Z (18 years, 10 months ago) by cebix
Branch: MAIN
CVS Tags: VERSION_4_2, HEAD
Changes since 1.8: +1 -1 lines
Log Message:
updated copyright dates

File Contents

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