ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/Frodo4/Src/IEC.cpp
Revision: 1.2
Committed: 2003-07-01T17:51:17Z (20 years, 9 months ago) by cebix
Branch: MAIN
Changes since 1.1: +1 -1 lines
Log Message:
updated copyright date

File Contents

# Content
1 /*
2 * IEC.cpp - IEC bus routines, 1541 emulation (DOS level)
3 *
4 * Frodo (C) 1994-1997,2002-2003 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 }