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