xref: /original-bsd/usr.bin/tn3270/ctlr/api.c (revision 208c3823)
1 /*
2  * Copyright (c) 1988 Regents of the University of California.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms are permitted
6  * provided that the above copyright notice and this paragraph are
7  * duplicated in all such forms and that any documentation,
8  * advertising materials, and other materials related to such
9  * distribution and use acknowledge that the software was developed
10  * by the University of California, Berkeley.  The name of the
11  * University may not be used to endorse or promote products derived
12  * from this software without specific prior written permission.
13  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
14  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
15  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
16  */
17 
18 #ifndef lint
19 static char sccsid[] = "@(#)api.c	4.4 (Berkeley) 10/31/89";
20 #endif /* not lint */
21 
22 /*
23  * This file implements the API used in the PC version.
24  */
25 
26 #include <stdio.h>
27 
28 #include "api.h"
29 #include "../general/general.h"
30 
31 #include "../api/disp_asc.h"
32 
33 #include "screen.h"
34 #include "hostctlr.h"
35 #include "oia.h"
36 
37 #include "../general/globals.h"
38 
39 int apitrace = 0;
40 
41 /*
42  * Some defines for things we use internally.
43  */
44 
45 #define	PS_SESSION_ID	23
46 #define	BUF_SESSION_ID	0
47 
48 /*
49  * General utility routines.
50  */
51 
52 #if	defined(MSDOS)
53 
54 #if	defined(LINT_ARGS)
55 static void movetous(char *, int, int, int);
56 static void movetothem(int, int, char *, int);
57 #endif	/* defined(LINT_ARGS) */
58 
59 #define	access_api(foo,length,copyin)	(foo)
60 #define	unaccess_api(foo,goo,length,copyout)
61 
62 static void
63 movetous(parms, es, di, length)
64 char *parms;
65 int es, di;
66 int length;
67 {
68     char far *farparms = parms;
69 
70     movedata(es, di, FP_SEG(farparms), FP_OFF(farparms), length);
71     if (apitrace) {
72 	Dump('(', parms, length);
73     }
74 }
75 
76 static void
77 movetothem(es, di, parms, length)
78 int es, di;
79 char *parms;
80 int length;
81 {
82     char far *farparms = parms;
83 
84     movedata(FP_SEG(farparms), FP_OFF(farparms), es, di, length);
85     if (apitrace) {
86 	Dump(')', parms, length);
87     }
88 }
89 #endif	/* defined(MSDOS) */
90 
91 #if	defined(unix)
92 extern char *access_api();
93 extern void movetous(), movetothem(), unaccess_api();
94 #endif	/* defined(unix) */
95 
96 
97 /*
98  * Supervisor Services.
99  */
100 
101 static void
102 name_resolution(regs, sregs)
103 union REGS *regs;
104 struct SREGS *sregs;
105 {
106     NameResolveParms parms;
107 
108     movetous((char *) &parms, sregs->es, regs->x.di, sizeof parms);
109 
110     regs->h.cl = 0;
111     if (memcmp((char *)&parms, NAME_SESSMGR, sizeof parms.gate_name) == 0) {
112 	regs->x.dx = GATE_SESSMGR;
113     } else if (memcmp((char *)&parms, NAME_KEYBOARD,
114 					sizeof parms.gate_name) == 0) {
115 	regs->x.dx = GATE_KEYBOARD;
116     } else if (memcmp((char *)&parms, NAME_COPY, sizeof parms.gate_name) == 0) {
117 	regs->x.dx = GATE_COPY;
118     } else if (memcmp((char *)&parms, NAME_OIAM, sizeof parms.gate_name) == 0) {
119 	regs->x.dx = GATE_OIAM;
120     } else {
121 	regs->h.cl = 0x2e;	/* Name not found */
122     }
123     regs->h.ch = 0x12;
124     regs->h.bh = 7;
125 }
126 
127 /*
128  * Session Information Services.
129  */
130 
131 static void
132 query_session_id(regs, sregs)
133 union REGS *regs;
134 struct SREGS *sregs;
135 {
136     QuerySessionIdParms parms;
137 
138     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
139 
140     if ((parms.rc != 0) || (parms.function_id != 0)) {
141 	parms.rc = 0x0c;
142     } else if (parms.option_code != 0x01) {
143 	parms.rc = 0x0d;	/* Invalid option code */
144 #ifdef	NOTOBS
145     } else if ((parms.data_code != 0x45) && (parms.data_code != 0x00/*OBS*/)) {
146 	parms.rc = 0x0b;
147 #endif	/* NOTOBS */
148     } else {
149 	NameArray list;
150 
151 	movetous((char *)&list, FP_SEG(parms.name_array),
152 		    FP_OFF(parms.name_array), sizeof list);
153 	if ((list.length < 14) || (list.length > 170)) {
154 	    parms.rc = 0x12;
155 	} else {
156 	    list.number_matching_session = 1;
157 	    list.name_array_element.short_name = parms.data_code;
158 	    list.name_array_element.type = TYPE_DFT;
159 	    list.name_array_element.session_id = PS_SESSION_ID;
160 	    memcpy(list.name_array_element.long_name, "ONLYSESS",
161 			    sizeof list.name_array_element.long_name);
162 	    movetothem(FP_SEG(parms.name_array),
163 		FP_OFF(parms.name_array), (char *)&list, sizeof list);
164 	    parms.rc = 0;
165 	}
166     }
167     parms.function_id = 0x6b;
168     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
169 }
170 
171 static void
172 query_session_parameters(regs, sregs)
173 union REGS *regs;
174 struct SREGS *sregs;
175 {
176     QuerySessionParametersParms parms;
177 
178     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
179 
180     if ((parms.rc !=0) || (parms.function_id != 0)) {
181 	parms.rc = 0x0c;
182     } else if (parms.session_id != PS_SESSION_ID) {
183 	parms.rc = 0x02;
184     } else {
185 	parms.rc = 0;
186 	parms.session_type = TYPE_DFT;
187 	parms.session_characteristics = 0;	/* Neither EAB nor PSS */
188 	parms.rows = MaxNumberLines;
189 	parms.columns = MaxNumberColumns;
190 	parms.presentation_space = 0;
191     }
192     parms.function_id = 0x6b;
193     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
194 }
195 
196 static void
197 query_session_cursor(regs, sregs)
198 union REGS *regs;
199 struct SREGS *sregs;
200 {
201     QuerySessionCursorParms parms;
202 
203     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
204 
205     if ((parms.rc != 0) || (parms.function_id != 0)) {
206 	parms.rc = 0x0c;
207     } else if (parms.session_id != PS_SESSION_ID) {
208 	parms.rc = 0x02;
209     } else {
210 	parms.rc = 0;
211 	parms.cursor_type = CURSOR_BLINKING;	/* XXX what is inhibited? */
212 	parms.row_address = ScreenLine(CursorAddress);
213 	parms.column_address = ScreenLineOffset(CursorAddress);
214     }
215 
216     parms.function_id = 0x6b;
217     movetothem(sregs->es, regs->x.di, (char *) &parms, sizeof parms);
218 }
219 
220 /*
221  * Keyboard Services.
222  */
223 
224 
225 static void
226 connect_to_keyboard(regs, sregs)
227 union REGS *regs;
228 struct SREGS *sregs;
229 {
230     ConnectToKeyboardParms parms;
231 
232     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
233 
234     if ((parms.rc != 0) || (parms.function_id != 0)) {
235 	parms.rc = 0x0c;
236     } else if (parms.session_id != PS_SESSION_ID) {
237 	parms.rc = 0x02;
238     } else if (parms.intercept_options != 0) {
239 	parms.rc = 0x01;
240     } else {
241 	parms.rc = 0;
242 	parms.first_connection_identifier = 0;
243     }
244     parms.function_id = 0x62;
245 
246     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
247 }
248 
249 static void
250 disconnect_from_keyboard(regs, sregs)
251 union REGS *regs;
252 struct SREGS *sregs;
253 {
254     DisconnectFromKeyboardParms parms;
255 
256     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
257 
258     if ((parms.rc != 0) || (parms.function_id != 0)) {
259 	parms.rc = 0x0c;
260     } else if (parms.session_id != PS_SESSION_ID) {
261 	parms.rc = 0x02;
262     } else if (parms.connectors_task_id != 0) {
263 	parms.rc = 04;			/* XXX */
264     } else {
265 	parms.rc = 0;
266     }
267     parms.function_id = 0x62;
268 
269     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
270 }
271 
272 static void
273 write_keystroke(regs, sregs)
274 union REGS *regs;
275 struct SREGS *sregs;
276 {
277     WriteKeystrokeParms parms;
278 
279     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
280 
281     if ((parms.rc != 0) || (parms.function_id != 0)) {
282 	parms.rc = 0x0c;
283     } else if (parms.session_id != PS_SESSION_ID) {
284 	parms.rc = 0x02;
285     } else if (parms.connectors_task_id != 0) {
286 	parms.rc = 0x04;
287     } else {
288 	parms.number_of_keys_sent = 0;
289 	parms.rc = 0;
290 	if (parms.options == OPTION_SINGLE_KEYSTROKE) {
291 	    KeystrokeEntry *entry = &parms.keystroke_specifier.keystroke_entry;
292 
293 	    if (AcceptKeystroke(entry->scancode, entry->shift_state) == 0) {
294 		parms.rc = 0x10;		/* XXX needs 0x12 too! */
295 	    }
296 	    parms.number_of_keys_sent++;
297 	} else if (parms.options == OPTION_MULTIPLE_KEYSTROKES) {
298 	    KeystrokeList
299 		list,
300 		far *atlist = parms.keystroke_specifier.keystroke_list;
301 	    KeystrokeEntry
302 		entry[10],		/* 10 at a time */
303 		*ourentry,
304 		far *theirentry;
305 	    int
306 		todo;
307 
308 	    movetous((char *)&list, FP_SEG(atlist),
309 			FP_OFF(atlist), sizeof *atlist);
310 	    todo = list.length/2;
311 	    ourentry = entry+(highestof(entry)+1);
312 	    theirentry = &atlist->keystrokes;
313 
314 	    while (todo) {
315 		if (ourentry > &entry[highestof(entry)]) {
316 		    int thistime;
317 
318 		    thistime = todo;
319 		    if (thistime > numberof(entry)) {
320 			thistime = numberof(entry);
321 		    }
322 		    movetous((char *)entry, FP_SEG(theirentry),
323 			    FP_OFF(theirentry), thistime*sizeof *theirentry);
324 		    theirentry += thistime;
325 		    ourentry = entry;
326 		}
327 		if (AcceptKeystroke(ourentry->scancode,
328 						ourentry->shift_state) == 0) {
329 		    parms.rc = 0x10;		/* XXX needs 0x12 too! */
330 		    break;
331 		}
332 		parms.number_of_keys_sent++;
333 		ourentry++;
334 		todo--;
335 	    }
336 	} else {
337 	    parms.rc = 0x01;
338 	}
339     }
340     parms.function_id = 0x62;
341 
342     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
343 /* XXX */
344 }
345 
346 
347 static void
348 disable_input(regs, sregs)
349 union REGS *regs;
350 struct SREGS *sregs;
351 {
352     DisableInputParms parms;
353 
354     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
355 
356     if ((parms.rc != 0) || (parms.function_id != 0)) {
357 	parms.rc = 0x0c;
358     } else if (parms.session_id != PS_SESSION_ID) {
359 	parms.rc = 0x02;
360     } else if (parms.connectors_task_id != 0) {
361 	parms.rc = 0x04;
362     } else {
363 	SetOiaApiInhibit(&OperatorInformationArea);
364 	parms.rc = 0;
365     }
366     parms.function_id = 0x62;
367 
368     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
369 }
370 
371 static void
372 enable_input(regs, sregs)
373 union REGS *regs;
374 struct SREGS *sregs;
375 {
376     EnableInputParms parms;
377 
378     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
379 
380     if ((parms.rc != 0) || (parms.function_id != 0)) {
381 	parms.rc = 0x0c;
382     } else if (parms.session_id != PS_SESSION_ID) {
383 	parms.rc = 0x02;
384     } else if (parms.connectors_task_id != 0) {
385 	parms.rc = 0x04;
386     } else {
387 	ResetOiaApiInhibit(&OperatorInformationArea);
388 	parms.rc = 0;
389     }
390     parms.function_id = 0x62;
391 
392     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
393 }
394 
395 /*
396  * Copy Services.
397  */
398 
399 static
400 copy_subroutine(target, source, parms, what_is_user, length)
401 BufferDescriptor *target, *source;
402 CopyStringParms *parms;
403 int what_is_user;
404 #define	USER_IS_TARGET	0
405 #define	USER_IS_SOURCE	1
406 {
407 #define	TARGET_NO_EAB		1
408 #define	SOURCE_NO_EAB		2
409 #define	TARGET_PC		4
410 #define	SOURCE_PC		8
411 #define	NO_FIELD_ATTRIBUTES	16
412     int needtodo = 0;
413     int access_length;
414     char far *input;
415     char far *output;
416     char far *access_pointer;
417 
418     if ((target->characteristics^source->characteristics)
419 		    &CHARACTERISTIC_EAB) {
420 	if (target->characteristics&CHARACTERISTIC_EAB) {
421 	    needtodo |= TARGET_NO_EAB;	/* Need to bump for EAB in target */
422 	} else {
423 	    needtodo |= SOURCE_NO_EAB;	/* Need to bump for EAB in source */
424 	}
425     }
426     if (target->session_type != source->session_type) {
427 	if (target->session_type == TYPE_PC) {
428 	    needtodo |= TARGET_PC;	/* scan codes to PC */
429 	} else {
430 	    needtodo |= SOURCE_PC;	/* PC to scan codes */
431 	}
432     }
433     if ((parms->copy_mode&COPY_MODE_FIELD_ATTRIBUTES) == 0) {
434 	needtodo |= NO_FIELD_ATTRIBUTES;
435     }
436     access_length = length;
437     if (what_is_user == USER_IS_TARGET) {
438 	if (target->characteristics&CHARACTERISTIC_EAB) {
439 	    access_length *= 2;
440 	}
441 	input = (char far *) &Host[source->begin];
442 	access_pointer = target->buffer;
443 	output = access_api(target->buffer, access_length, 0);
444     } else {
445 	if (source->characteristics&CHARACTERISTIC_EAB) {
446 	    access_length *= 2;
447 	}
448 	access_pointer = source->buffer;
449 	input = access_api(source->buffer, access_length, 1);
450 	output = (char far *) &Host[target->begin];
451     }
452     while (length--) {
453 	if (needtodo&TARGET_PC) {
454 	    *output++ = disp_asc[*input++];
455 	} else if (needtodo&SOURCE_PC) {
456 	    *output++ = asc_disp[*input++];
457 	} else {
458 	    *output++ = *input++;
459 	}
460 	if (needtodo&TARGET_NO_EAB) {
461 	    input++;
462 	} else if (needtodo&SOURCE_NO_EAB) {
463 	    *output++ = 0;		/* Should figure out good EAB? */
464 	}
465     }
466     if (what_is_user == USER_IS_TARGET) {
467 	unaccess_api(target->buffer, access_pointer, access_length, 1);
468     } else {
469 	unaccess_api(source->buffer, access_pointer, access_length, 0);
470     }
471 }
472 
473 
474 static void
475 copy_string(regs, sregs)
476 union REGS *regs;
477 struct SREGS *sregs;
478 {
479     CopyStringParms parms;
480     BufferDescriptor *target = &parms.target, *source = &parms.source;
481     int length;
482 
483     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
484 
485     length = 1+parms.source_end-source->begin;
486     if ((parms.rc != 0) || (parms.function_id !=0)) {
487 	parms.rc = 0x0c;
488     } else if (target->session_id == BUF_SESSION_ID) {	/* Target is buffer */
489 	if (source->session_id != PS_SESSION_ID) {		/* A no-no */
490 	    parms.rc = 0x2;
491 	} else {
492 	    if ((source->begin < 0) || (source->begin > highestof(Host))) {
493 		parms.rc = 0x06;		/* invalid source definition */
494 	    } else {
495 		if ((source->begin+length) > highestof(Host)) {
496 		    length = highestof(Host)-source->begin;
497 		    parms.rc = 0x0f;	/* Truncate */
498 		}
499 	        if ((source->characteristics == target->characteristics) &&
500 		    (source->session_type == target->session_type)) {
501 		    if (source->characteristics&CHARACTERISTIC_EAB) {
502 			length *= 2;
503 		    }
504 		    movetothem(FP_SEG(target->buffer),
505 			    FP_OFF(target->buffer),
506 			    (char *)&Host[source->begin], length);
507 		} else {
508 		    copy_subroutine(target, source, &parms,
509 							USER_IS_TARGET, length);
510 		}
511 	    }
512 	}
513     } else if (source->session_id != BUF_SESSION_ID) {
514 	    parms.rc = 0xd;
515     } else {
516 	/* Send to presentation space (3270 buffer) */
517 	if ((target->begin < 0) || (target->begin > highestof(Host))) {
518 	    parms.rc = 0x07;		/* invalid target definition */
519 	} if (!UnLocked) {
520 		parms.rc = 0x03;	/* Keyboard locked */
521 	} else if (parms.copy_mode != 0) {
522 		parms.rc = 0x0f;	/* Copy of field attr's not allowed */
523 	} else if (IsProtected(target->begin) || /* Make sure no protected */
524 		    (WhereAttrByte(target->begin) !=	/* in range */
525 			    WhereAttrByte(target->begin+length-1))) {
526 		parms.rc = 0x0e;	/* Attempt to write in protected */
527 	} else {
528 	    if ((target->begin+length) > highestof(Host)) {
529 		length = highestof(Host)-target->begin;
530 		parms.rc = 0x0f;	/* Truncate */
531 	    }
532 	    TurnOnMdt(target->begin);	/* Things have changed */
533 	    if ((source->characteristics == target->characteristics) &&
534 		    (source->session_type == target->session_type)) {
535 		if (source->characteristics&CHARACTERISTIC_EAB) {
536 		    length *= 2;
537 		}
538 		movetous((char *)&Host[target->begin],
539 			    FP_SEG(source->buffer),
540 			    FP_OFF(source->buffer), length);
541 	    } else {
542 		copy_subroutine(target, source, &parms, USER_IS_SOURCE, length);
543 	    }
544 	}
545     }
546     parms.function_id = 0x64;
547     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
548 }
549 
550 
551 /*
552  * Operator Information Area Services.
553  */
554 
555 static void
556 read_oia_group(regs, sregs)
557 union REGS *regs;
558 struct SREGS *sregs;
559 {
560     ReadOiaGroupParms parms;
561 
562     movetous((char *)&parms, sregs->es, regs->x.di, sizeof parms);
563 
564     if ((parms.rc != 0) || (parms.function_id != 0)) {
565 	parms.rc = 0x0c;
566     } else if (parms.session_id != PS_SESSION_ID) {
567 	parms.rc = 0x02;
568     } else {
569 	int group = parms.oia_group_number;
570 	char *from;
571 	int size;
572 
573 	if ((group != API_OIA_ALL_GROUPS) &&
574 		((group > API_OIA_LAST_LEGAL_GROUP) || (group < 0))) {
575 	} else {
576 	    if (group == API_OIA_ALL_GROUPS) {
577 		size = API_OIA_BYTES_ALL_GROUPS;
578 		from = (char *)&OperatorInformationArea;
579 	    } else if (group == API_OIA_INPUT_INHIBITED) {
580 		size = sizeof OperatorInformationArea.input_inhibited;
581 		from = (char *)&OperatorInformationArea.input_inhibited[0];
582 	    } else {
583 		size = 1;
584 		from = ((char *)&OperatorInformationArea)+group;
585 	    }
586 	    movetothem(FP_SEG(parms.oia_buffer), FP_OFF(parms.oia_buffer),
587 			from, size);
588 	}
589     }
590     parms.function_id = 0x6d;
591     movetothem(sregs->es, regs->x.di, (char *)&parms, sizeof parms);
592 }
593 
594 /*ARGSUSED*/
595 static void
596 unknown_op(regs, sregs)
597 union REGS *regs;
598 struct SREGS *sregs;
599 {
600     regs->h.ch = 0x12;
601     regs->h.cl = 0x05;
602 }
603 
604 
605 handle_api(regs, sregs)
606 union REGS *regs;
607 struct SREGS *sregs;
608 {
609 /*
610  * Do we need to log this transaction?
611  */
612     if (apitrace) {
613 	Dump('<', (char *)regs, sizeof *regs);
614 	Dump('<', (char *)sregs, sizeof *sregs);
615     }
616     if (regs->h.ah == NAME_RESOLUTION) {
617 	name_resolution(regs, sregs);
618 #if	defined(unix)
619     } else if (regs->h.ah == PS_OR_OIA_MODIFIED) {
620 	while ((oia_modified == 0) && (ps_modified == 0)) {
621 	    (void) Scheduler(1);
622 	}
623 	oia_modified = ps_modified = 0;
624 #endif	/* defined(unix) */
625     } else if (regs->h.ah != 0x09) {
626 	regs->h.ch = 0x12;
627 	regs->h.cl = 0x0f;		/* XXX Invalid environmental access */
628     } else if (regs->x.bx != 0x8020) {
629 	regs->h.ch = 0x12;
630 	regs->h.cl = 0x08;		/* XXX Invalid wait specified */
631     } else if (regs->h.ch != 0) {
632 	regs->x.cx = 0x1206;		/* XXX Invalid priority */
633     } else {
634 	switch (regs->x.dx) {
635 	case GATE_SESSMGR:
636 	    switch (regs->h.al) {
637 	    case QUERY_SESSION_ID:
638 		if (regs->h.cl != 0) {
639 		    regs->x.cx = 0x1206;
640 		} else {
641 		    regs->x.cx = 0x1200;
642 		    query_session_id(regs, sregs);
643 		}
644 		break;
645 	    case QUERY_SESSION_PARAMETERS:
646 		if (regs->h.cl != 0) {
647 		    regs->x.cx = 0x1206;
648 		} else {
649 		    regs->x.cx = 0x1200;
650 		    query_session_parameters(regs, sregs);
651 		}
652 		break;
653 	    case QUERY_SESSION_CURSOR:
654 		if ((regs->h.cl != 0xff) && (regs->h.cl != 0x00/*OBS*/)) {
655 		    regs->x.cx = 0x1206;
656 		} else {
657 		    regs->x.cx = 0x1200;
658 		    query_session_cursor(regs, sregs);
659 		}
660 		break;
661 	    default:
662 		unknown_op(regs, sregs);
663 		break;
664 	    }
665 	    break;
666 	case GATE_KEYBOARD:
667 	    if (regs->h.cl != 00) {
668 		regs->x.cx = 0x1206;
669 	    } else {
670 		regs->x.cx = 0x1200;
671 		switch (regs->h.al) {
672 		case CONNECT_TO_KEYBOARD:
673 		    connect_to_keyboard(regs, sregs);
674 		    break;
675 		case DISABLE_INPUT:
676 		    disable_input(regs, sregs);
677 		    break;
678 		case WRITE_KEYSTROKE:
679 		    write_keystroke(regs, sregs);
680 		    break;
681 		case ENABLE_INPUT:
682 		    enable_input(regs, sregs);
683 		    break;
684 		case DISCONNECT_FROM_KEYBOARD:
685 		    disconnect_from_keyboard(regs, sregs);
686 		    break;
687 		default:
688 		    unknown_op(regs, sregs);
689 		    break;
690 		}
691 	    }
692 	    break;
693 	case GATE_COPY:
694 	    if (regs->h.cl != 0xff) {
695 		regs->x.cx = 0x1206;
696 	    } else {
697 		regs->x.cx = 0x1200;
698 		switch (regs->h.al) {
699 		case COPY_STRING:
700 		    copy_string(regs, sregs);
701 		    break;
702 		default:
703 		    unknown_op(regs, sregs);
704 		    break;
705 		}
706 	    }
707 	    break;
708 	case GATE_OIAM:
709 	    if (regs->h.cl != 0xff) {
710 		regs->x.cx = 0x1206;
711 	    } else {
712 		regs->x.cx = 0x1200;
713 		switch (regs->h.al) {
714 		case READ_OIA_GROUP:
715 		    read_oia_group(regs, sregs);
716 		    break;
717 		default:
718 		    unknown_op(regs, sregs);
719 		    break;
720 		}
721 	    }
722 	    break;
723 	default:
724 	    regs->h.ch = 0x12;
725 	    regs->h.cl = 0x34;		/* Invalid GATE entry */
726 	    break;
727 	}
728     }
729 /*
730  * Do we need to log this transaction?
731  */
732     if (apitrace) {
733 	Dump('>', (char *)regs, sizeof *regs);
734 	Dump('>', (char *)sregs, sizeof *sregs);
735 #ifdef	MSDOS
736 	{ char buf[10];  gets(buf); }
737 #endif	/* MSDOS */
738     }
739 }
740