ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/IEC.cpp
Revision: 1.1
Committed: 2003-07-01T17:09:43Z (20 years, 9 months ago) by cebix
Branch: MAIN
Log Message:
imported files

File Contents

# User Rev Content
1 cebix 1.1 /*
2     * IEC.cpp - IEC bus routines, 1541 emulation (DOS level)
3     *
4     * Frodo (C) 1994-1997,2002 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 D64Drive(this, ThePrefs.DrivePath[i]);
72     else
73     drive[i] = new T64Drive(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 D64Drive(this, prefs->DrivePath[i]);
124     else
125     drive[i] = new T64Drive(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);
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     char *Errors_1541[] = {
416     "00, OK,00,00\r",
417     "25,WRITE ERROR,00,00\r",
418     "26,WRITE PROTECT ON,00,00\r",
419     "30,SYNTAX ERROR,00,00\r",
420     "33,SYNTAX ERROR,00,00\r",
421     "60,WRITE FILE OPEN,00,00\r",
422     "61,FILE NOT OPEN,00,00\r",
423     "62,FILE NOT FOUND,00,00\r",
424     "67,ILLEGAL TRACK OR SECTOR,00,00\r",
425     "70,NO CHANNEL,00,00\r",
426     "73,CBM DOS V2.6 1541,00,00\r",
427     "74,DRIVE NOT READY,00,00\r"
428     };
429    
430     void Drive::set_error(int error)
431     {
432     error_ptr = Errors_1541[error];
433     error_len = strlen(error_ptr);
434    
435     // Set drive condition
436     if (error != ERR_OK)
437     if (error == ERR_STARTUP)
438     LED = DRVLED_OFF;
439     else
440     LED = DRVLED_ERROR;
441     else if (LED == DRVLED_ERROR)
442     LED = DRVLED_OFF;
443     the_iec->UpdateLEDs();
444     }