1 /*
2 * parallel-trap.c
3 *
4 * Written by
5 * Andreas Boose <viceteam@t-online.de>
6 * Andre Fachat <a.fachat@physik.tu-chemnitz.de>
7 *
8 * This file is part of VICE, the Versatile Commodore Emulator.
9 * See README for copyright notice.
10 *
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
15 *
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
20 *
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
24 * 02111-1307 USA.
25 *
26 */
27
28 #include "vice.h"
29
30 #include <stdio.h>
31
32 #include "attach.h"
33 #include "drive.h"
34 #include "drivetypes.h"
35 #include "log.h"
36 #include "parallel-trap.h"
37 #include "parallel.h"
38 #include "serial.h"
39 #include "types.h"
40
41
42 #define SERIAL_NAMELENGTH 255
43
44
45 /* On which channel did listen happen to? */
46 static uint8_t TrapDevice;
47 static uint8_t TrapSecondary;
48
49 /* Function to call when EOF happens in `serialreceivebyte()'. */
50 static void (*eof_callback_func)(void);
51
52 /* Function to call when the `serialattention()' trap is called. */
53 static void (*attention_callback_func)(void);
54
55 /* Logging goes here. */
56 static log_t parallel_log = LOG_DEFAULT;
57
58 static uint8_t SerialBuffer[SERIAL_NAMELENGTH + 1];
59 static int SerialPtr;
60
61 /*
62 On a real system an opened channel is affected only after having
63 received and parsed the complete next open command.
64 This patch tries to emulate this behavior and allows to keep the
65 status channel continuously open while opening and closing other
66 files on the same device.
67
68 FIXME: test for regressions and either revert to, or remove old code
69 */
70 #define DELAYEDCLOSE
71
parallelcommand(void)72 static int parallelcommand(void)
73 {
74 serial_t *p;
75 uint8_t b;
76 int channel;
77 int i, st = 0;
78 void *vdrive;
79 unsigned int dnr;
80
81 dnr = TrapDevice & 0x0f;
82 if (dnr >= DRIVE_UNIT_MIN &&
83 dnr < DRIVE_UNIT_MIN+NUM_DISK_UNITS &&
84 diskunit_context[dnr - DRIVE_UNIT_MIN]->enable) {
85 return PAR_STATUS_DEVICE_NOT_PRESENT + /* device not present */
86 PAR_STATUS_TIME_OUT_ON_WRITE +
87 PAR_STATUS_TIME_OUT_ON_READ;
88 }
89
90 /* which device ? */
91 p = serial_device_get(TrapDevice & 0x0f);
92 /* TODO: drive 1? */
93 vdrive = (void *)file_system_get_vdrive(TrapDevice & 0x0f, 0);
94 channel = TrapSecondary & 0x0f;
95
96 /* if command on a channel, reset output buffer... */
97 if ((TrapSecondary & 0xf0) != 0x60) {
98 p->nextok[channel] = 0;
99 p->lastok[channel] = 0;
100 }
101 switch (TrapSecondary & 0xf0) {
102 case 0x60: /* Secondary address */
103 /* Open Channel */
104 if (p->isopen[channel] == ISOPEN_CLOSED) {
105 p->isopen[channel] = ISOPEN_OPEN;
106 st = (*(p->openf))(vdrive, NULL, 0, channel, NULL);
107 for (i = 0; i < SerialPtr; i++) {
108 (*(p->putf))(vdrive, SerialBuffer[i], channel);
109 }
110 SerialPtr = 0;
111 }
112 if (p->flushf) {
113 (*(p->flushf))(vdrive, channel);
114 }
115
116 if ((!st) && ((TrapDevice & 0xf0) == 0x40)) { /* isTalking */
117 /* any error, except eof */
118 st = parallel_trap_receivebyte(&b, 1) & 0xbf;
119 }
120 break;
121 case 0xE0:
122 /* Close File */
123 p->isopen[channel] = ISOPEN_CLOSED;
124 st = (*(p->closef))(vdrive, channel);
125 break;
126 case 0xF0:
127 /* Open File */
128 if (p->isopen[channel] != ISOPEN_CLOSED) {
129 #ifndef DELAYEDCLOSE
130 if (p->isopen[channel] == ISOPEN_OPEN) {
131 log_warning(parallel_log, "Bogus close?");
132 (*(p->closef))(vdrive, channel);
133 }
134 p->isopen[channel] = ISOPEN_OPEN;
135 SerialBuffer[SerialPtr] = 0;
136 st = (*(p->openf))(vdrive, SerialBuffer, SerialPtr, channel, NULL);
137 SerialPtr = 0;
138
139 if (st) {
140 p->isopen[channel] = ISOPEN_CLOSED;
141 (*(p->closef))(vdrive, channel);
142 log_error(parallel_log, "Cannot open file. Status $%02x.", st);
143 }
144 #else /* DELAYEDCLOSE */
145 if (SerialPtr != 0 || channel == 0x0f) {
146 (*(p->closef))(vdrive, channel);
147 p->isopen[channel] = ISOPEN_OPEN;
148
149 SerialBuffer[SerialPtr] = 0;
150 st = (*(p->openf))(vdrive, SerialBuffer, SerialPtr, channel, NULL);
151 SerialPtr = 0;
152
153 if (st) {
154 p->isopen[channel] = ISOPEN_CLOSED;
155 (*(p->closef))(vdrive, channel);
156 log_error(parallel_log,
157 "Cannot open file. Status $%02x.",
158 (unsigned int)st);
159 }
160 }
161 #endif /* DELAYEDCLOSE */
162 }
163 if (p->flushf) {
164 (*(p->flushf))(vdrive, channel);
165 }
166 break;
167 default:
168 log_error(parallel_log, "Unknown command %02X.", TrapSecondary & 0xff);
169 }
170 return st;
171 }
172
parallel_trap_attention(int b)173 int parallel_trap_attention(int b)
174 {
175 int st = 0;
176 serial_t *p;
177 void *vdrive;
178
179 #ifdef DEBUG
180 if (debug.ieee) {
181 log_message(parallel_log, "ParallelAttention(%02x).", (unsigned int)b);
182 }
183 #endif
184
185 if (b == 0x3f /* unlisten */
186 && (((TrapSecondary & 0xf0) == 0xf0) /* open filename */
187 || ((TrapSecondary & 0x0f) == 0x0f))) { /* secaddr #15 */
188 st = parallelcommand();
189 } else {
190 switch (b & 0xf0) {
191 case 0x20: /* Listen + device */
192 case 0x40: /* Talk + device */
193 /* If this device is already emulated with TDE, don't
194 * try to react to it here. */
195 {
196 int dnr = b & 0x0f;
197 if (dnr >= DRIVE_UNIT_MIN &&
198 dnr < DRIVE_UNIT_MIN+NUM_DISK_UNITS &&
199 diskunit_context[dnr - DRIVE_UNIT_MIN]->enable) {
200 } else {
201 TrapDevice = b;
202 }
203 }
204 break;
205
206 case 0x60: /* Secondary address */
207 case 0xe0: /* Close a file */
208 if (TrapDevice != 0) {
209 TrapSecondary = b;
210 st |= parallelcommand();
211 }
212 break;
213
214 case 0xf0: /* Open File; needs the filename first */
215 if (TrapDevice != 0) {
216 TrapSecondary = b;
217 p = serial_device_get(TrapDevice & 0x0f);
218 #ifndef DELAYEDCLOSE
219 /* TODO drive 1? */
220 vdrive = (void *)file_system_get_vdrive(TrapDevice & 0x0f, 0);
221 if (p->isopen[b & 0x0f] == ISOPEN_OPEN) {
222 (*(p->closef))(vdrive, b & 0x0f);
223 }
224 #endif
225 p->isopen[b & 0x0f] = ISOPEN_AWAITING_NAME;
226 }
227 break;
228 }
229 }
230
231 if (TrapDevice != 0) {
232 p = serial_device_get(TrapDevice & 0x0f);
233 if (!(p->inuse)) {
234 st |= PAR_STATUS_DEVICE_NOT_PRESENT;
235 }
236
237 /* If it was a listen or talk or secondary addr or unlisten */
238 if (((b & 0xf0) == 0x20) || ((b & 0xf0) == 0x40) || ((b & 0xf0) == 0x60)
239 || (b == 0x3f)) {
240 if (p->listenf) {
241 /* send talk/listen/unlisten to emulated devices for
242 flushing of REL file write buffer. */
243 if ((TrapDevice & 0x0f) >= DRIVE_UNIT_MIN) {
244 /* TODO drive 1? */
245 vdrive = (void *)file_system_get_vdrive(TrapDevice & 0x0f, 0);
246 (*(p->listenf))(vdrive, TrapSecondary & 0x0f);
247 }
248 }
249 }
250 }
251
252 if ((b == 0x3f) || (b == 0x5f)) {
253 TrapDevice = 0;
254 TrapSecondary = 0;
255 }
256
257 st |= TrapDevice << 8;
258
259 if (attention_callback_func) {
260 attention_callback_func();
261 }
262
263 return st;
264 }
265
parallel_trap_sendbyte(uint8_t data)266 int parallel_trap_sendbyte(uint8_t data)
267 {
268 int st = 0;
269 serial_t *p;
270 void *vdrive;
271 unsigned int dnr;
272
273 dnr = TrapDevice & 0x0f;
274 if (dnr >= DRIVE_UNIT_MIN &&
275 dnr < DRIVE_UNIT_MIN+NUM_DISK_UNITS &&
276 diskunit_context[dnr - DRIVE_UNIT_MIN]->enable) {
277 return PAR_STATUS_DEVICE_NOT_PRESENT + /* device not present */
278 PAR_STATUS_TIME_OUT_ON_WRITE +
279 PAR_STATUS_TIME_OUT_ON_READ;
280 }
281
282 p = serial_device_get(TrapDevice & 0x0f);
283 /* TODO drive 1 */
284 vdrive = (void *)file_system_get_vdrive(TrapDevice & 0x0f, 0);
285
286 if (p->inuse) {
287 if (p->isopen[TrapSecondary & 0x0f] == ISOPEN_AWAITING_NAME) {
288 #ifdef DEBUG
289 if (debug.ieee) {
290 log_message(parallel_log,
291 "SerialSendByte[%2d] = %02x.", SerialPtr, data);
292 }
293 #endif
294 /* Store name here */
295 if (SerialPtr < SERIAL_NAMELENGTH) {
296 SerialBuffer[SerialPtr++] = data;
297 }
298 } else {
299 /* Send to device */
300 st = (*(p->putf))(vdrive, data, TrapSecondary & 0x0f);
301 }
302 } else { /* Not present */
303 st = PAR_STATUS_DEVICE_NOT_PRESENT +
304 PAR_STATUS_TIME_OUT_ON_WRITE +
305 PAR_STATUS_TIME_OUT_ON_READ;
306 }
307
308 return st + (TrapDevice << 8);
309 }
310
parallel_trap_receivebyte(uint8_t * data,int fake)311 int parallel_trap_receivebyte(uint8_t *data, int fake)
312 {
313 int st = 0;
314 int secadr = TrapSecondary & 0x0f;
315 serial_t *p;
316 void *vdrive;
317 unsigned int dnr;
318
319 dnr = TrapDevice & 0x0f;
320 if (dnr >= DRIVE_UNIT_MIN &&
321 dnr < DRIVE_UNIT_MIN+NUM_DISK_UNITS &&
322 diskunit_context[dnr - DRIVE_UNIT_MIN]->enable) {
323 return 0x83; /* device not present */
324 }
325
326 p = serial_device_get(TrapDevice & 0x0f);
327 /* TODO: drive 1 */
328 vdrive = (void *)file_system_get_vdrive(TrapDevice & 0x0f, 0);
329
330 /* first fill up buffers */
331 #if 0
332 if (!p->lastok[secadr]) {
333 p->lastok[secadr] = p->nextok[secadr];
334 p->lastbyte[secadr] = p->nextbyte[secadr];
335 p->lastst[secadr] = p->nextst[secadr];
336 p->nextok[secadr] = 0;
337 #endif
338 if (!p->lastok[secadr]) {
339 p->lastst[secadr] =
340 (*(p->getf))(vdrive, &(p->lastbyte[secadr]), secadr);
341 p->lastok[secadr] = 1;
342 }
343 #if 0
344 }
345 if ((!p->nextok[secadr]) && (!p->lastst[secadr])) {
346 p->nextst[secadr] =
347 (*(p->getf))(vdrive, &(p->nextbyte[secadr]), secadr);
348 p->nextok[secadr] = 1;
349 }
350 #endif
351 *data = p->lastbyte[secadr];
352 if (!fake) {
353 p->lastok[secadr] = 0;
354 }
355 #if 0
356 st = p->nextok[secadr] ? p->nextst[secadr] :
357 (p->lastok[secadr] ? p->lastst[secadr] : 2);
358 #endif
359 st = p->lastst[secadr]; /* added */
360 st += TrapDevice << 8;
361
362 #ifdef DEBUG
363 if (debug.ieee) {
364 log_message(parallel_log,
365 "receive: sa=%02x lastb = %02x (data=%02x), "
366 "ok=%s, st=%04x, nextb = %02x, "
367 "ok=%s, st=%04x.",
368 (unsigned int)secadr,
369 p->lastbyte[secadr],
370 (int)*data,
371 p->lastok[secadr] ? "ok" : "no",
372 (unsigned int)(p->lastst[secadr]),
373 p->nextbyte[secadr],
374 p->nextok[secadr] ? "ok" : "no",
375 (unsigned int)(p->nextst[secadr]));
376 }
377 #endif
378 #if 0
379 if ((!fake) && p->nextok[secadr] && p->nextst[secadr]) {
380 p->nextok[secadr] = 0;
381 }
382 #endif
383 if ((st & 0x40) && eof_callback_func != NULL) {
384 eof_callback_func();
385 }
386 return st;
387 }
388
389 /* Specify a function to call when EOF happens in `serialreceivebyte()'. */
parallel_trap_eof_callback_set(void (* func)(void))390 void parallel_trap_eof_callback_set(void (*func)(void))
391 {
392 eof_callback_func = func;
393 }
394
395 /* Specify a function to call when the `serialattention()' trap is called. */
parallel_trap_attention_callback_set(void (* func)(void))396 void parallel_trap_attention_callback_set(void (*func)(void))
397 {
398 attention_callback_func = func;
399 }
400