|
|
1.1 root 1: /*
2: * Keyboard/display driver.
3: * Coherent, IBM PC/XT/AT.
4: */
5: #include <sys/coherent.h>
6: #ifdef _I386
7: #include <sys/reg.h>
8: #else
9: #include <sys/i8086.h>
10: #endif
11: #include <sys/con.h>
12: #include <sys/devices.h>
13: #include <errno.h>
14: #include <sys/stat.h>
15: #include <sys/tty.h>
16: #include <signal.h>
17: #include <sys/sched.h>
18: #include <sys/silo.h>
19: #include <sys/kb.h>
20: #include <sys/vt.h>
21:
22: #define ISVEC 1 /* Keyboard interrupt vector */
23: #define DEBUG 0
24:
25: #define SPC 0376 /* Special encoding */
26: #define XXX 0377 /* Non-character */
27: #define KBDATA 0x60 /* Keyboard data */
28: #define KBCTRL 0x61 /* Keyboard control */
29: #define KBFLAG 0x80 /* Keyboard reset flag */
30: #define LEDCMD 0xED /* status indicator command */
31: #define KBACK 0xFA /* status indicator acknowledge */
32: #define EXTENDED1 0xE1 /* extended key seq initiator */
33:
34: #define KEYUP 0x80 /* Key up change */
35: #define KEYSC 0x7F /* Key scan code mask */
36: #define LSHIFT 0x2A-1 /* Left shift key */
37: #define LSHIFTA 0x2B-1 /* Alternate left-shift key */
38: #define RSHIFT 0x36-1 /* Right shift key */
39: #define CTRLkb 0x1D-1 /* Control key */
40: /*-- #define CAPLOCK 0x1D-1 --*/ /* Control key */
41: #define ALTkb 0x38-1 /* Alt key */
42: #define CAPLOCK 0x3A-1 /* Caps lock key */
43: /*-- #define CTRL 0x3A-1 --*/ /* Caps lock key */
44: #define NUMLOCK 0x45-1 /* Numeric lock key */
45: #define DELETE 0x53-1 /* Del, as in CTRL-ALT-DEL */
46: #define BACKSP 0x0E-1 /* Back space */
47: #define SCRLOCK 0x46-1 /* Scroll lock */
48:
49: /* Shift flags */
50: #define SRS 0x01 /* Right shift key on */
51: #define SLS 0x02 /* Left shift key on */
52: #define CTS 0x04 /* Ctrl key on */
53: #define ALS 0x08 /* Alt key on */
54: #define CPLS 0x10 /* Caps lock on */
55: #define NMLS 0x20 /* Num lock on */
56: #define AKPS 0x40 /* Alternate keypad shift */
57: #define SHFT 0x80 /* Shift key flag */
58:
59: /*
60: * patchable params for non-standard keyboards
61: */
62: int KBTIMEOUT = 10000; /* shouldn't need this much */
63: int KBSTS_CMD = 0x64; /* Keyboard status/command */
64:
65: /* Function key information */
66: #define NFKEY 20 /* Number of settable functions */
67: #define NFCHAR 150 /* Number of characters settable */
68: #define NFBUF (NFKEY*2+NFCHAR+1) /* Size of buffer */
69: /*
70: * Functions.
71: */
72: int isrint();
73: int istime();
74: void isbatch();
75: int mmstart();
76: int isopen();
77: int isclose();
78: int isread();
79: int mmwrite();
80: int isioctl();
81: void mmwatch();
82: int isload();
83: int isuload();
84: int ispoll();
85: int nulldev();
86: int nonedev();
87:
88: /*
89: * Configuration table.
90: */
91: CON iscon ={
92: DFCHR|DFPOL, /* Flags */
93: KB_MAJOR, /* Major index */
94: isopen, /* Open */
95: isclose, /* Close */
96: nulldev, /* Block */
97: isread, /* Read */
98: mmwrite, /* Write */
99: isioctl, /* Ioctl */
100: nulldev, /* Powerfail */
101: mmwatch, /* Timeout */
102: isload, /* Load */
103: isuload, /* Unload */
104: ispoll /* Poll */
105: };
106:
107: /*
108: * Flag indicating turbo machine.
109: */
110: int isturbo = 0;
111:
112:
113: /*
114: ==============================================================================
115: ==============================================================================
116: */
117: /* constants for vtdata[] */
118: #define VT_VGAPORT 0x3D4
119: #define VT_MONOPORT 0x3B4
120:
121: #ifdef _I386
122: #define VT_MONOBASE SEG_VIDEOa
123: #define VT_VGABASE SEG_VIDEOb
124: #else
125: #define VT_MONOBASE 0xB000
126: #define VT_VGABASE 0xB800
127: #endif
128:
129: /*
130: Patchable table entrys,
131: we go indirect in order to produce a label which can be addressed
132: */
133: HWentry VTVGA = { 4, 0, VT_VGAPORT, { 0, VT_VGABASE }, { 25, 80 } };
134: HWentry VTMONO = { 4, 0, VT_MONOPORT, { 0, VT_MONOBASE }, { 25, 80 } };
135:
136: HWentry *vtHWtable[] = {
137: VTVGA, /* VGA followed by MONO is compatible to DOS */
138: VTMONO,
139: 0 /* MUST STAY AS LAST ELEMENT !!! */
140: };
141:
142: extern int mminit();
143: static VTDATA const_vtdata = {
144: mminit, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0, 23, 24, 0, 0, 0, 23, 0, 0, 1
145: };
146:
147: /* later this should be dynamic */
148: VTDATA *vtconsole, **vtdata;
149:
150: int vtcount, vtmax;
151: extern int vtactive;
152: int vt_verbose = { 0 };
153: int vt_opened = { 0 };
154:
155: /* Terminal structure. */
156: TTY **vttty;
157:
158: /*
159: ==============================================================================
160: ==============================================================================
161: */
162:
163: static silo_t in_silo;
164:
165: /*
166: * State variables.
167: */
168: int islock; /* Keyboard locked flag */
169: int isbusy; /* Raw input conversion busy */
170: static unsigned shift; /* Overall shift state */
171: static char scrollkb; /* Scroll lock state */
172: static char lshiftkb = LSHIFT; /* Left shift alternate state */
173: static char isfbuf[NFBUF]; /* Function key values */
174: static char *isfval[NFKEY]; /* Function key string pointers */
175: static int ledcmd; /* LED update command flag */
176: static int extended; /* extended key scan count */
177: static char fk_loaded; /* true == funcion keys resident */
178:
179: /*
180: * Tables for converting key code to ASCII.
181: * lmaptab specifies unshifted conversion,
182: * umaptab specifies shifted conversion,
183: * smaptab specifies the shift states which are active.
184: * An entry of XXX says the key is dead.
185: * An entry of SPC requires further processing.
186: *
187: * Key codes:
188: * ESC .. <- == 1 .. 14
189: * -> .. \n == 15 .. 28
190: * CTRL .. ` == 29 .. 41
191: * ^Shift .. PrtSc == 42 .. 55
192: * ALT .. CapsLock == 56 .. 58
193: * F1 .. F10 == 59 .. 68
194: * NumLock .. Del == 69 .. 83
195: */
196: static unsigned char lmaptab[] ={
197: '\33', '1', '2', '3', '4', '5', '6', /* 1 - 7 */
198: '7', '8', '9', '0', '-', '=', '\b', '\t', /* 8 - 15 */
199: 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', /* 16 - 23 */
200: 'o', 'p', '[', ']', '\r', XXX, 'a', 's', /* 24 - 31 */
201: 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', /* 32 - 39 */
202: '\'', '`', XXX, '\\', 'z', 'x', 'c', 'v', /* 40 - 47 */
203: 'b', 'n', 'm', ',', '.', '/', XXX, '*', /* 48 - 55 */
204: XXX, ' ', XXX, SPC, SPC, SPC, SPC, SPC, /* 56 - 63 */
205: SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, /* 64 - 71 */
206: SPC, SPC, '-', SPC, SPC, SPC, '+', SPC, /* 72 - 79 */
207: SPC, SPC, SPC, SPC /* 80 - 83 */
208: };
209:
210: static unsigned char umaptab[] ={
211: '\33', '!', '@', '#', '$', '%', '^', /* 1 - 7 */
212: '&', '*', '(', ')', '_', '+', '\b', SPC, /* 8 - 15 */
213: 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', /* 16 - 23 */
214: 'O', 'P', '{', '}', '\r', XXX, 'A', 'S', /* 24 - 31 */
215: 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', /* 32 - 39 */
216: '"', '~', XXX, '|', 'Z', 'X', 'C', 'V', /* 40 - 47 */
217: 'B', 'N', 'M', '<', '>', '?', XXX, '*', /* 48 - 55 */
218: XXX, ' ', XXX, SPC, SPC, SPC, SPC, SPC, /* 56 - 63 */
219: SPC, SPC, SPC, SPC, SPC, SPC, SPC, SPC, /* 64 - 71 */
220: SPC, SPC, '-', SPC, SPC, SPC, '+', SPC, /* 72 - 79 */
221: SPC, SPC, SPC, SPC /* 80 - 83 */
222: };
223:
224: #define SS0 0 /* No shift */
225: #define SS1 (SLS|SRS|CTS) /* Shift, Ctrl */
226: #define SES (SLS|SRS) /* Shift */
227: #define LET (SLS|SRS|CPLS|CTS) /* Shift, Caps, Ctrl */
228: #define KEY (SLS|SRS|NMLS|AKPS) /* Shift, Num, Alt keypad */
229:
230: static unsigned char smaptab[] ={
231: SS0, SES, SS1, SES, SES, SES, SS1, /* 1 - 7 */
232: SES, SES, SES, SES, SS1, SES, CTS, SES, /* 8 - 15 */
233: LET, LET, LET, LET, LET, LET, LET, LET, /* 16 - 23 */
234: LET, LET, SS1, SS1, CTS, SHFT, LET, LET, /* 24 - 31 */
235: LET, LET, LET, LET, LET, LET, LET, SES, /* 32 - 39 */
236: SES, SS1, SHFT, SS1, LET, LET, LET, LET, /* 40 - 47 */
237: LET, LET, LET, SES, SES, SES, SHFT, SES, /* 48 - 55 */
238: SHFT, SS1, SHFT, SS0, SS0, SS0, SS0, SS0, /* 56 - 63 */
239: SS0, SS0, SS0, SS0, SS0, SHFT, KEY, KEY, /* 64 - 71 */
240: KEY, KEY, SS0, KEY, KEY, KEY, SS0, KEY, /* 72 - 79 */
241: KEY, KEY, KEY, KEY /* 80 - 83 */
242: };
243:
244: /*
245: * Load entry point.
246: * Do reset the keyboard because it gets terribly munged
247: * if you type during the boot.
248: */
249: isload()
250: {
251: register short i; /* was: int i */
252: register HWentry **hw;
253: register VTDATA *vp;
254:
255: /*
256: * Reset keyboard if NOT an XT turbo.
257: */
258: if ( ! isturbo ) {
259: outb(KBCTRL, 0x0C); /* Clock low */
260: for (i = 10582; --i >= 0; ); /* For 20ms */
261: outb(KBCTRL, 0xCC); /* Clock high */
262: for (i = 0; --i != 0; )
263: ;
264: i = inb(KBDATA);
265: outb(KBCTRL, 0xCC); /* Clear keyboard */
266: outb(KBCTRL, 0x4D); /* Enable keyboard */
267: }
268: PRINTV("vtload:\n");
269: fk_loaded = 0;
270:
271: /* figure out what our current max is */
272: for( vtmax = 0, hw = vtHWtable; *hw; ++hw ) {
273: vtmax += (*hw)->count;
274: (*hw)->found = 0; /* assume non-exist */
275: }
276: PRINTV( "vtload: %d screens possible\n", vtmax );
277:
278: vtdata = (VTDATA **) kalloc( vtmax * sizeof( *vtdata ) );
279: if( vtdata == NULL ) {
280: printf( "vtload: unable to obtain vtdata[%d]\n", vtmax );
281: u.u_error = -1;
282: return;
283: }
284: PRINTV( "vtload: obtained vtdata[%d] @%x\n", vtmax, vtdata );
285:
286: vttty = (TTY **) kalloc( vtmax * sizeof( *vttty ) );
287: if( vttty == NULL ) {
288: printf( "vtload: unable to obtain vttty[%d]\n", vtmax );
289: u.u_error = -1;
290: return;
291: }
292: PRINTV( "vtload: obtained vttty[%d] @%x\n", vtmax, vttty );
293:
294: /* determine which video adaptors are present */
295: for( vtcount = 0, hw = vtHWtable; *hw; ++hw ) {
296: /* remember our logical start */
297: (*hw)->start = vtcount;
298: PRINTV( ", start %d\n", vtcount );
299:
300: /* allocate the necessary memory */
301: for ( i = 0; i < (*hw)->count; ++i ) {
302: vp = vtdata[vtcount] = kalloc( sizeof(VTDATA) );
303: PRINTV( " vtdata[%d] = @%x\n", vtcount, vp );
304: if( vp == NULL || !VTttyinit(vtcount) ) {
305: printf("not enough memory for VTDATA\n" );
306: break;
307: }
308:
309: /* fill in appropriately */
310: *vp = const_vtdata;
311: vp->vmm_port = (*hw)->port;
312: vp->vmm_vseg = (*hw)->vidmemory.seg;
313: vp->vmm_voff = (*hw)->vidmemory.off;
314:
315: vp->vt_ind = vtcount;
316: vtdatainit(vp);
317: if (i == 0 ) {
318: vp->vmm_visible = VNKB_TRUE;
319: vp->vmm_seg = vp->vmm_vseg;
320: vp->vmm_off = vp->vmm_voff;
321: updscreen(vtcount);
322: }
323: (*hw)->found++;
324: vtcount++;
325: }
326: }
327:
328: /*
329: * initialize vtconsole
330: */
331: vtconsole = vtdata[vtactive = 0];
332: vtconsole->vmm_invis = 0; /* vtconsole cursor visible */
333:
334: /*
335: * Seize keyboard interrupt.
336: */
337: #ifdef _I386
338: setivec(ISVEC, isrint);
339: #else
340: #if VT_MAJOR == KB_MAJOR
341: setivec(1, isrint);
342: #else
343:
344: /*
345: * Map table and vector to us
346: */
347: i = sphi();
348: PRINTV( "VTload: unload old vector\n" );
349: kcall( Kclrivec, 1 );
350: setivec(1, isrint);
351: spl( i );
352: #endif
353: #endif /* _I386 */
354:
355: /*
356: * Enable mmwatch() invocation every second.
357: */
358: drvl[VT_MAJOR].d_time = 1;
359:
360: /*
361: * Initialize video display.
362: */
363: for ( i = 0; i < vtcount; ++i )
364: mmstart( vttty[i] );
365: }
366:
367: /*
368: * Unload entry point.
369: */
370: isuload()
371: {
372: register int i;
373: register level = sphi();
374:
375: clrivec(ISVEC);
376: #ifndef _I386
377: #if VT_MAJOR != KB_MAJOR
378: kcall( Ksetivec, ISVEC, &Kisrint );
379: #endif
380: #endif
381: spl( level );
382:
383: /* Restore pointers to original state. */
384: vtconsole = vtdata[0];
385: vtconsole->vmm_invis = 0;
386: vtconsole->vmm_visible = VNKB_TRUE;
387:
388: if( vt_opened )
389: printf( "VTclose with %d open screens\n", vt_opened );
390:
391: #ifndef _I386
392: for( i = 0; i < vtcount; ++i ) {
393: PRINTV( "VTuload: free far %x:%x, tty %x\n",
394: vttty[i]->t_buffer->s_faddr, vttty[i] );
395: sfree( vttty[i]->t_buffer );
396: kfree( vttty[i] );
397: sfree( vtdata[i].vt_buffer );
398: }
399: #endif
400: }
401:
402: /*
403: * Default function key strings (terminated by -1 [\377])
404: */
405: static char *deffuncs[] = {
406: "\33[1x\377", /* F1 */
407: "\33[2x\377", /* F2 */
408: "\33[3x\377", /* F3 */
409: "\33[4x\377", /* F4 */
410: "\33[5x\377", /* F5 */
411: "\33[6x\377", /* F6 */
412: "\33[7x\377", /* F7 */
413: "\33[8x\377", /* F8 */
414: "\33[9x\377", /* F9 */
415: "\33[0x\377", /* F10 - historical value */
416: "\33[1y\377", /* F11 */
417: "\33[2y\377", /* F12 */
418: "\33[3y\377", /* F13 */
419: "\33[4y\377", /* F14 */
420: "\33[5y\377", /* F15 */
421: "\33[6y\377", /* F16 */
422: "\33[7y\377", /* F17 */
423: "\33[8y\377", /* F18 */
424: "\33[9y\377", /* F19 */
425: "\33[0y\377" /* F20 */
426: };
427:
428: /*
429: * Open routine.
430: */
431: isopen(dev, mode)
432: dev_t dev;
433: unsigned int mode;
434: {
435: register int s;
436: register TTY *tp;
437: int index = vtindex(dev);
438:
439: PRINTV("isopen: %x\n", dev);
440: if (index < 0 || index >= vtcount) {
441: u.u_error = ENXIO;
442: return;
443: }
444:
445: tp = vttty[index];
446: if ((tp->t_flags&T_EXCL) != 0 && !super()) {
447: u.u_error = ENODEV;
448: return;
449: }
450: ttsetgrp(tp, dev, mode);
451:
452: s = sphi();
453: if (tp->t_open++ == 0) {
454: initkeys(); /* init function keys */
455: tp->t_flags = T_CARR; /* indicate "carrier" */
456: ttopen(tp);
457: }
458: spl(s);
459: #if 0
460: updleds(); /* update keyboard status LEDS */
461: #endif
462: }
463:
464: /* Init function keys */
465: initkeys()
466: { register int i;
467: register char *cp1, *cp2;
468:
469: for (i=0; i < NFKEY; i++)
470: isfval[i] = 0; /* clear function key buffer */
471: cp2 = isfbuf; /* pointer to key buffer */
472: for (i=0; i < NFKEY; i++) {
473: isfval[i] = cp2; /* save pointer to key string */
474: cp1 = deffuncs[i]; /* get init string pointer */
475: while ((*cp2++ = *cp1++) != -1) /* copy key data */
476: if (cp2 >= &isfbuf[NFBUF-3]) /* overflow? */
477: return;
478: }
479: }
480:
481: void isvtswitch(); /* deferred virtual terminal switch */
482:
483: /*
484: * Close a tty.
485: */
486: isclose(dev)
487: {
488: register int s;
489: int index = vtindex(dev);
490: register TTY *tp = vttty[index];
491:
492: if (--tp->t_open == 0)
493: ttclose(tp);
494: }
495:
496:
497: /*
498: * Read routine.
499: */
500: isread(dev, iop)
501: dev_t dev;
502: IO *iop;
503: {
504: int index = vtindex(dev);
505: register TTY *tp = vttty[index];
506:
507: ttread(tp, iop, 0);
508: if (tp->t_oq.cq_cc)
509: mmtime(tp);
510: }
511:
512: /*
513: * Ioctl routine.
514: */
515: isioctl(dev, com, vec)
516: dev_t dev;
517: struct sgttyb *vec;
518: {
519: register int s;
520:
521: switch(com) {
522: case TIOCSETF:
523: case TIOCGETF:
524: isfunction(com, (char *)vec);
525: break;
526: case TIOCSHIFT: /* switch left-SHIFT and "\" */
527: lshiftkb = LSHIFTA; /* alternate values */
528: lmaptab[41] = '\\';
529: lmaptab[42] = XXX;
530: umaptab[41] = '|';
531: umaptab[42] = XXX;
532: smaptab[41] = SS1;
533: smaptab[42] = SHFT;
534: break;
535: case TIOCCSHIFT: /* normal (default) left-SHIFT and "\" */
536: lshiftkb = LSHIFT; /* normal values */
537: lmaptab[41] = XXX;
538: lmaptab[42] = '\\';
539: umaptab[41] = XXX;
540: umaptab[42] = '|';
541: smaptab[41] = SHFT;
542: smaptab[42] = SS1;
543: break;
544: default:
545: s = sphi();
546: ttioctl(vttty[vtindex(dev)], com, vec);
547: spl(s);
548: break;
549: }
550: }
551:
552: /*
553: * Set and receive the function keys.
554: */
555: isfunction(c, v)
556: int c;
557: char *v;
558: {
559: register char *cp;
560: register int i;
561:
562: if (c == TIOCGETF) {
563: for (cp = isfbuf; cp < &isfbuf[NFBUF]; cp++)
564: putubd(v++, *cp);
565: } else {
566: for (i=0; i < NFKEY; i++) /* zap current settings */
567: isfval[i] = 0;
568: cp = isfbuf; /* pointer to key buffer */
569: for (i=0; i<NFKEY; i++) {
570: isfval[i] = cp; /* save pointer to key string */
571: while ((*cp++ = getubd(v++)) != -1) /* copy key data */
572: if (cp >= &isfbuf[NFBUF-3]) /* overflow? */
573: return;
574: }
575: }
576: }
577:
578:
579: /*
580: * Poll routine.
581: */
582: ispoll( dev, ev, msec )
583: dev_t dev;
584: int ev;
585: int msec;
586: {
587: register TTY *tp = vttty[vtindex(dev)];
588:
589: return ttpoll(tp, ev, msec);
590: }
591:
592: /*
593: * Receive interrupt.
594: */
595: isrint()
596: {
597: register int c;
598: register int s;
599: register int r;
600: int savests;
601: int update_leds = 0;
602:
603: /*
604: * Schedule raw input handler if not already active.
605: */
606: if ( isbusy == 0 ) {
607: isbusy = 1;
608: defer(isbatch, vttty[vtactive]);
609: }
610:
611: /*
612: * Pull character from the data
613: * port. Pulse the KBFLAG in the control
614: * port to reset the data buffer.
615: */
616: r = inb(KBDATA) & 0xFF;
617: c = inb(KBCTRL);
618: outb(KBCTRL, c|KBFLAG);
619: outb(KBCTRL, c);
620: #if KBDEBUG
621: printf("kbd: %d\n", r); /* print scan code/direction */
622: #endif
623: if (ledcmd) {
624: ledcmd = 0;
625: if (r == KBACK) { /* output to status LEDS */
626: c = scrollkb & 1;
627: if (shift & NMLS)
628: c |= 2;
629: if (shift & CPLS)
630: c |= 4;
631: outb(KBDATA, c);
632: }
633: return;
634: }
635: if (extended > 0) { /* if multi-character seq, */
636: --extended; /* ... ignore this char */
637: return;
638: }
639: if (r == EXTENDED1) { /* ignore extended sequences */
640: extended = 5;
641: return;
642: }
643: if (r == 0xFF)
644: return; /* Overrun */
645: c = (r & KEYSC) - 1;
646: /*
647: * Check for reset.
648: */
649: if ((r&KEYUP) == 0 && c == DELETE && (shift&(CTS|ALS)) == (CTS|ALS))
650: boot();
651:
652: /*
653: * Track "shift" keys.
654: */
655: s = smaptab[c];
656: if (s & SHFT) {
657: if (r & KEYUP) { /* "shift" released */
658: if (c == RSHIFT)
659: shift &= ~SRS;
660: else if (c == lshiftkb)
661: shift &= ~SLS;
662: else if (c == CTRLkb)
663: shift &= ~CTS;
664: else if (c == ALTkb)
665: shift &= ~ALS;
666: } else { /* "shift" pressed */
667: if (c == lshiftkb)
668: shift |= SLS;
669: else if (c == RSHIFT)
670: shift |= SRS;
671: else if (c == CTRLkb)
672: shift |= CTS;
673: else if (c == ALTkb)
674: shift |= ALS;
675: else if (c == CAPLOCK) {
676: shift ^= CPLS; /* toggle cap lock */
677: updleds();
678: } else if (c == NUMLOCK) {
679: shift ^= NMLS; /* toggle num lock */
680: updleds();
681: }
682: }
683: return;
684: }
685:
686: /*
687: * No other key up codes of interest.
688: */
689: if (r & KEYUP)
690: return;
691:
692: /*
693: * Map character, based on the
694: * current state of the shift, control,
695: * meta and lock flags.
696: */
697: if (shift & CTS) {
698: if (s == CTS) /* Map Ctrl (BS | NL) */
699: c = (c == BACKSP) ? 0x7F : 0x0A;
700: else if (s==SS1 || s==LET) /* Normal Ctrl map */
701: c = umaptab[c]&0x1F; /* Clear bits 5-6 */
702: else { if (s==KEY || s==SS0)
703: vtnumeric(r);
704: return; /* Ignore this char */
705: }
706: } else if (s &= shift) {
707: if (shift & SES) { /* if shift on */
708: if (s & (CPLS|NMLS)) /* if caps/num lock */
709: c = lmaptab[c]; /* use unshifted */
710: else
711: c = umaptab[c]; /* use shifted */
712: } else { /* if shift not on */
713: if (s & (CPLS|NMLS)) /* if caps/num lock */
714: c = umaptab[c]; /* use shifted */
715: else
716: c = lmaptab[c]; /* use unshifted */
717: }
718: } else
719: c = lmaptab[c]; /* use unshifted */
720:
721: /*
722: * Act on character.
723: */
724: if (c == XXX)
725: return; /* char to ignore */
726:
727: if (c != SPC) { /* not special char? */
728: if (shift & ALS) /* ALT (meta bit)? */
729: c |= 0x80; /* set meta */
730: isin(c); /* send the char */
731: } else
732: update_leds += isspecial(r); /* special chars */
733: if (update_leds) {
734: savests = sphi();
735: outb(KBDATA, LEDCMD);
736: ledcmd = 1;
737: spl(savests);
738: }
739: }
740:
741: /*
742: * Process numeric keypad for virtual terminals.
743: */
744: vtnumeric(c)
745: int c;
746: {
747: switch (c) {
748: case 71: case 72: case 73: /* ctrl 7/8/9 (vt7, vt8, vt9) */
749: defer(isvtswitch, c + 16);
750: break;
751: case 74: /* ctrl - */
752: defer(isvtswitch, vtp);
753: break;
754: case 75: case 76: case 77: /* ctrl 4/5/6 (vt5, vt6, vt7) */
755: defer(isvtswitch, c + 10);
756: break;
757: case 78: /* ctrl + */
758: defer(isvtswitch, vtn);
759: break;
760: case 79: case 80: case 81: /* ctrl 1/2/3 */
761: defer(isvtswitch, c + 2);
762: break;
763: case 82: /* ctrl 0 (vt0) */
764: defer(isvtswitch, vt0);
765: break;
766: case 83: /* ctrl del (toggle) */
767: c = vtt;
768: defer(isvtswitch, vtt);
769: break;
770: }
771: }
772:
773: /*
774: * Handle special input sequences.
775: * The character passed is the key number.
776: *
777: * The keypad is translated by the following table,
778: * the first entry is the normal sequence, the second the shifted,
779: * and the third the alternate keypad sequence.
780: */
781: static char *keypad[][3] = {
782: { "\33[H", "7", "\33?w" }, /* 71 */
783: { "\33[A", "8", "\33?x" }, /* 72 */
784: { "\33[V", "9", "\33?y" }, /* 73 */
785: { "\33[D", "4", "\33?t" }, /* 75 */
786: { "\0337", "5", "\33?u" }, /* 76 */
787: { "\33[C", "6", "\33?v" }, /* 77 */
788: { "\33[24H","1", "\33?q" }, /* 79 */
789: { "\33[B", "2", "\33?r" }, /* 80 */
790: { "\33[U", "3", "\33?s" }, /* 81 */
791: { "\33[@", "0", "\33?p" }, /* 82 */
792: { "\33[P", ".", "\33?n" } /* 83 */
793: };
794:
795: isspecial(c)
796: int c;
797: {
798: register char *cp;
799: register int s;
800: int update_leds = 0;
801:
802: cp = 0;
803:
804: switch (c) {
805: case 15: /* cursor back tab */
806: cp = "\033[Z";
807: break;
808: case 59: case 60: case 61: case 62: case 63: /* Function keys */
809: case 64: case 65: case 66:
810: /* offset to function string */
811: /* Magic numbers 21 and 61 to mach vtnkb constants */
812: if ( shift & ALS )
813: defer(isvtswitch, c + 21);
814: else
815: cp = isfval[c-59];
816: break;
817: case 67: case 68:
818: /* offset to function string */
819: if ( shift & ALS )
820: defer(isvtswitch, c + 61);
821: else
822: cp = isfval[c-59];
823: break;
824: case 70: /* Scroll Lock -- stop/start output */
825: {
826: static char cbuf[2];
827:
828: cp = &cbuf[0]; /* working buffer */
829: if (!(vttty[vtactive]->t_sgttyb.sg_flags
830: & RAWIN)) { /* not if in RAW mode */
831: ++update_leds;
832: if (vttty[vtactive]->t_flags&T_STOP){/* output stopped? */
833: /* start it */
834: cbuf[0] = vttty[vtactive]->t_tchars.t_startc;
835: scrollkb = 0;
836: } else { /* stop output */
837: cbuf[0] = vttty[vtactive]->t_tchars.t_stopc;
838: scrollkb = 1;
839: }
840: }
841: break;
842: }
843:
844: case 79: /* 1/End */
845: case 80: /* 2/DOWN */
846: case 81: /* 3/PgDn */
847: case 82: /* 0/Ins */
848: case 83: /* ./Del */
849: --c; /* adjust code */
850: case 75: /* 4/LEFT */
851: case 76: /* 5 */
852: case 77: /* 6/RIGHT */
853: --c; /* adjust code */
854: case 71: /* 7/Home/Clear */
855: case 72: /* 8/UP */
856: case 73: /* 9/PgUp */
857: s = 0; /* start off with normal keypad */
858: if (shift&NMLS) /* num lock? */
859: s = 1; /* set shift pad */
860: if (shift&SES) /* shift? */
861: s ^= 1; /* toggle shift pad */
862: if (shift&AKPS) /* alternate pad? */
863: s = 2; /* set alternate pad */
864: cp = keypad[c-71][s]; /* get keypad value */
865: break;
866: }
867: if (cp) /* send string */
868: while ((*cp != 0) && (*cp != -1))
869: isin( *cp++ & 0377 );
870: return update_leds;
871: }
872:
873: /**
874: *
875: * void
876: * ismmfunc( c ) -- process keyboard related output escape sequences
877: * char c;
878: */
879: void
880: ismmfunc(c)
881: register int c;
882: {
883: switch (c) {
884: case 't': /* Enter numlock */
885: shift |= NMLS;
886: updleds(); /* update LED status */
887: break;
888: case 'u': /* Leave numlock */
889: shift &= ~NMLS;
890: updleds(); /* update LED status */
891: break;
892: case '=': /* Enter alternate keypad */
893: shift |= AKPS;
894: break;
895: case '>': /* Exit alternate keypad */
896: shift &= ~AKPS;
897: break;
898: case 'c': /* Reset terminal */
899: islock = 0;
900: shift = 0;
901: initkeys();
902: updleds(); /* update LED status */
903: break;
904: }
905: }
906:
907: /**
908: *
909: * void
910: * isin( c ) -- append character to raw input silo
911: * char c;
912: */
913: static
914: isin( c )
915: register int c;
916: {
917: int cache_it = 1;
918: TTY * tp = vttty[vtactive];
919:
920: /*
921: * If using software incoming flow control, process and
922: * discard t_stopc and t_startc.
923: */
924: if (ISIXON) {
925: #if _I386
926: if (ISSTART || (ISIXANY && ISXSTOP)) {
927: tp->t_flags &= ~(T_STOP | T_XSTOP);
928: ttstart(tp);
929: cache_it = 0;
930: } else if (ISSTOP) {
931: if ((tp->t_flags&T_STOP) == 0)
932: tp->t_flags |= (T_STOP | T_XSTOP);
933: cache_it = 0;
934: }
935: #else
936: if (ISSTOP) {
937: if ((tp->t_flags&T_STOP) == 0)
938: tp->t_flags |= T_STOP;
939: cache_it = 0;
940: }
941: if (ISSTART) {
942: tp->t_flags &= ~T_STOP;
943: ttstart(tp);
944: cache_it = 0;
945: }
946: #endif
947: }
948:
949: /*
950: * If the tty is not open the character is
951: * just tossed away.
952: */
953: if (vttty[vtactive]->t_open == 0)
954: return;
955:
956: /*
957: * Cache received character.
958: */
959: if (cache_it) {
960: in_silo.si_buf[ in_silo.si_ix ] = c;
961:
962: if ( ++in_silo.si_ix >= sizeof(in_silo.si_buf) )
963: in_silo.si_ix = 0;
964: }
965: }
966:
967: /**
968: *
969: * void
970: * isbatch() -- raw input conversion routine
971: *
972: * Action: Enable the video display.
973: * Canonize the raw input silo.
974: *
975: * Notes: isbatch() was scheduled as a deferred process by isrint().
976: */
977: static void
978: isbatch( tp )
979: register TTY * tp;
980: {
981: register int c;
982: static int lastc;
983: VTDATA *vp = tp->t_ddp;
984:
985: /*
986: * Ensure video display is enabled.
987: */
988: if (vp->vmm_visible)
989: mm_von(vp);
990:
991: isbusy = 0;
992:
993: /*
994: * Process all cached characters.
995: */
996: while ( in_silo.si_ix != in_silo.si_ox ) {
997: /*
998: * Get next cached char.
999: */
1000: c = in_silo.si_buf[ in_silo.si_ox ];
1001:
1002: if ( in_silo.si_ox >= sizeof(in_silo.si_buf) - 1 )
1003: in_silo.si_ox = 0;
1004: else
1005: in_silo.si_ox++;
1006:
1007: if ( (islock == 0) || ISINTR || ISQUIT ) {
1008: ttin( tp, c );
1009: }
1010:
1011: else if ( (c == 'b') && (lastc == '\033') ) {
1012: islock = 0;
1013: ttin( tp, lastc );
1014: ttin( tp, c );
1015: }
1016:
1017: else if ( (c == 'c') && (lastc == '\033') ) {
1018: ttin( tp, lastc );
1019: ttin( tp, c );
1020: }
1021:
1022: else
1023: putchar('\007');
1024:
1025: lastc = c;
1026: }
1027: }
1028:
1029: /*
1030: * update the keyboard status LEDS
1031: */
1032: updleds()
1033: {
1034: int s;
1035:
1036: s = sphi();
1037: outb(KBDATA, LEDCMD);
1038: ledcmd = 1;
1039: spl(s);
1040: }
1041:
1042: /*
1043: * unlock the scroll in case an interrupt character is received
1044: */
1045: kbunscroll()
1046: {
1047: scrollkb = 0;
1048: updleds();
1049: }
1050:
1051: int
1052: VTttyinit(i)
1053: int i;
1054: {
1055: TTY *tp;
1056:
1057: /*
1058: * get pointer to TTY structure from kernal memory space
1059: */
1060: if( (tp = vttty[i] = (TTY *)kalloc(sizeof (TTY))) == NULL )
1061: return(0);
1062: PRINTV( " vttty[%d]: @%x, ", i, tp );
1063:
1064: #if FAR_TTY
1065: /*
1066: * get pointers to the buffers pointed to by the TTY structure
1067: * from user memory space
1068: */
1069: tp->t_buffer = salloc( (fsize_t)NCIB+2*SI_BUFSIZ, SFSYST|SFNSWP );
1070: tp->t_ib = 0;
1071: tp->t_rawin.si_buf = NCIB;
1072: tp->t_rawout.si_buf = NCIB+SI_BUFSIZ;
1073: #endif
1074: tp->t_param = NULL;
1075: tp->t_start = &mmstart;
1076:
1077: #ifndef _I386
1078: #if VT_MAJOR == KB_MAJOR
1079: tp->t_cs_sel = 0;
1080: #else
1081: tp->t_cs_sel = cs_sel();
1082: #endif
1083: #endif
1084: tp->t_ddp = vtdata[i];
1085: PRINTV( "data @%lx\n", tp->t_ddp );
1086: return(1);
1087: }
1088:
1089: vtdatainit(vp)
1090: VTDATA *vp;
1091: {
1092: #ifndef _I386
1093: VT_FARSEG vt_farseg;
1094: #endif
1095: /*
1096: * vtdata init - vmm part
1097: */
1098: vp->vmm_invis = -1; /* cursor invisible */
1099:
1100: #ifdef _I386
1101: vp->vt_buffer = kalloc( TEXTBLOCK );
1102: vp->vmm_seg = vp->vmm_mseg = ds_sel();
1103: vp->vmm_off = vp->vmm_moff = vp->vt_buffer;
1104: #else
1105: vp->vt_buffer = salloc ( (fsize_t)TEXTBLOCK, SFSYST|SFNSWP|SFHIGH );
1106: vp->vmm_seg = vp->vmm_mseg = FP_SEG( vp->vt_buffer->vt_faddr );
1107: vp->vmm_off = vp->vmm_moff = FP_OFF( vp->vt_buffer->vt_faddr );
1108: #endif
1109: PRINTV( "vt@%x init index %d,%d), seg %x, off %x\n",
1110: vp, vp->vt_ind, vp->vmm_mseg, vp->vmm_moff );
1111: /*
1112: * vtdata init - vnkb part
1113: */
1114: /* Make the first memory block active, if present */
1115: vp->vnkb_lastc = 0;
1116: vp->vnkb_fnkeys = 0;
1117: vp->vnkb_funkeyp = 0;
1118: vp->vnkb_fk_loaded = 0; /* no Fn keys yet */
1119: }
1120:
1121: /*
1122: * Given device number, return index for vtdata[], vttty[], etc.
1123: *
1124: * Major number must be VT_MAJOR for CPU to get here.
1125: *
1126: * Minor Number Index Value
1127: * ----- ------ ----- -----
1128: * 0000 0000 vtactive ... device (2,0) is the active screen
1129: * 0000 0001 0
1130: * 0000 0010 1
1131: * 0000 0011 2
1132: * ....
1133: * 0000 1111 14
1134: *
1135: * 0100 xxxx xxxx ... color devices only
1136: * 0101 xxxx xxxx - (# of color devices found) ... monochrome only
1137: *
1138: * Return value is in range 0 to vtcount-1 for valid minor numbers,
1139: * -1 for invalid minor numbers.
1140: */
1141: int
1142: vtindex( dev )
1143: dev_t dev;
1144: {
1145: register int ret = -1;
1146:
1147: if ( dev & VT_PHYSICAL ) {
1148: int hw = ( dev >> 4 ) & 3;
1149: int hw_index = dev & 0x0F;
1150:
1151: if( hw_index < vtHWtable[hw]->found )
1152: ret = vtHWtable[hw]->start + hw_index;
1153: } else {
1154: int lg_index = dev & 0x0F;
1155:
1156: if (lg_index == 0)
1157: ret = vtactive;
1158: if (lg_index > 0 && lg_index <= vtcount )
1159: ret = lg_index-1;
1160: }
1161: if (ret >= 0)
1162: ret %= vtcount;
1163: else
1164: PRINTV( "vtindex: (%x) %d. invalid !\n", dev, ret );
1165: return ret;
1166: }
1167:
1168: /*
1169: *
1170: * void
1171: * isvtswitch() -- deferred virtual terminal switch
1172: *
1173: * Action: - save current shift key status
1174: * - determine new active virtual terminal
1175: * - deactivate shift key status of the current virtual terminal
1176: * - deactivate current virtual terminal
1177: * - activate shift key status of the new virtual terminal with
1178: * the previously saved shift key status
1179: * - activate new virtual terminal
1180: *
1181: * Notes: isvtswitch() was scheduled as a deferred process by
1182: * process_key() which is a function called by isrint().
1183: */
1184: void
1185: isvtswitch(key_val)
1186: {
1187: register int new_index, i;
1188: unsigned lockshift, nolockshift;
1189: VTDATA *vp = vtdata[vtactive];
1190: VTDATA *vp_old, *vp_new;
1191: static int vtprevious;
1192:
1193: lockshift = shift & (CPLS | NMLS);
1194: nolockshift = shift & ~(CPLS | NMLS);
1195: PRINTV( "F%d: %d", key_val, vtactive );
1196:
1197: switch (key_val) {
1198: case VTKEY_HOME:
1199: new_index = 0;
1200: break;
1201: case VTKEY_NEXT:
1202: new_index = vtactive;
1203: for( i = 0; i < vtcount; ++i ) {
1204: new_index = ++new_index % vtcount;
1205: if( vttty[new_index]->t_open )
1206: break;
1207: }
1208: break;
1209: case VTKEY_PREV:
1210: new_index = vtactive;
1211: for( i = 0; i < vtcount; ++i ) {
1212: new_index = (--new_index+vtcount) % vtcount;
1213: if( vttty[new_index]->t_open )
1214: break;
1215: }
1216: break;
1217: case VTKEY_TOGL:
1218: new_index = vtprevious;
1219: break;
1220: default:
1221: new_index = vtindex(vtkey_to_dev(key_val));
1222: if( new_index < 0) {
1223: putchar( '\007' );
1224: return;
1225: }
1226: }
1227:
1228: T_CON(8, printf("%d->%d ", vtactive, new_index));
1229: if( new_index == vtactive )
1230: return;
1231:
1232: /* Save which locking shift states are in effect. */
1233:
1234: vp_old = vtdata[vtactive];
1235: vp_new = vtdata[new_index];
1236:
1237: vp_old->vnkb_shift = lockshift;
1238: vtdeactivate(vp_new, vp_old); /* deactivate old virtual terminal */
1239:
1240: /* Restore shift lock state, append current momentary shift state. */
1241: shift = vp_new->vnkb_shift | nolockshift;
1242:
1243: vtactivate(vp_new); /* activate new virtual terminal */
1244: updterminal(new_index);
1245: vtprevious = vtactive;
1246: vtactive = new_index; /* update vtactive */
1247: }
1248:
1249: vtdeactivate(vp_new, vp_old)
1250: register VTDATA *vp_new, *vp_old;
1251: {
1252: register i;
1253: VTDATA *vpi;
1254:
1255: /* store old screen contents in memory segment */
1256: FFCOPY( vp_old->vmm_voff, vp_old->vmm_vseg,
1257: vp_old->vmm_moff, vp_old->vmm_mseg, TEXTBLOCK );
1258:
1259: /*
1260: * if changing to another screen on same video board
1261: * for all screens on same board as new screen
1262: * deactivate, but don't update
1263: * else - changing to a screen on different board
1264: * for all screens NOT on same board as new screen
1265: * deactivate, but don't update
1266: */
1267: if ( vp_old->vmm_port == vp_new->vmm_port ) {
1268: T_CON(8, printf("deactivate on %x ", vp_new->vmm_port));
1269: for (i = 0; i < vtcount; ++i) {
1270: vpi = vtdata[i];
1271: if ( vpi->vmm_port == vp_new->vmm_port ) {
1272: /* deactivate, but don't update */
1273: vpi->vmm_invis = ~0;
1274: vpi->vmm_visible = VNKB_FALSE;
1275: vpi->vmm_seg = vpi->vmm_mseg;
1276: vpi->vmm_off = vpi->vmm_moff;
1277: if( vpi->vmm_seg == 0 )
1278: printf( "[1]vpi->vmm_seg = 0\n" );
1279: PRINTV( "vt.back %d. seg %x off %x\n", i,
1280: vpi->vmm_seg, vpi->vmm_off );
1281: }
1282: }
1283: } else {
1284: T_CON(8, printf("deactivate %x->%x ",
1285: vp_old->vmm_port, vp_new->vmm_port));
1286: for (i = 0; i < vtcount; ++i) {
1287: vpi = vtdata[i];
1288: if ( (vpi->vmm_port != vp_new->vmm_port)
1289: && (vpi->vmm_invis == 0) ) {
1290: /* update, but don't deactivate */
1291: vpi->vmm_invis = ~0;
1292: updscreen(i);
1293: }
1294: }
1295: }
1296: }
1297:
1298: vtactivate(vp)
1299: VTDATA *vp;
1300: {
1301: register VTDATA *vpi;
1302: register i;
1303:
1304: /*
1305: * copy from screen contents from heap segment to video memory
1306: * only if necessary
1307: */
1308: if ( vp->vmm_visible == VNKB_FALSE )
1309: FFCOPY( vp->vmm_moff, vp->vmm_mseg,
1310: vp->vmm_voff, vp->vmm_vseg, TEXTBLOCK );
1311:
1312: for (i = 0; i < vtcount; ++i) {
1313: vpi = vtdata[i];
1314: if (vpi->vmm_port == vp->vmm_port) {
1315: vpi->vmm_invis = -1;
1316: vpi->vmm_visible = VNKB_FALSE;
1317: vpi->vmm_seg = vpi->vmm_mseg;
1318: vpi->vmm_off = vpi->vmm_moff;
1319: if( vpi->vmm_seg == 0 )
1320: printf( "[2]vpi->vmm_seg = 0\n" );
1321: PRINTV( "vt.back seg %x off %x\n",
1322: vpi->vmm_seg, vpi->vmm_off );
1323: }
1324: }
1325: /*
1326: * Set new active terminal
1327: */
1328: vp->vmm_invis = 0;
1329: vp->vmm_visible = VNKB_TRUE;
1330: vp->vmm_seg = vp->vmm_vseg;
1331: vp->vmm_off = vp->vmm_voff;
1332: if( vp->vmm_seg == 0 )
1333: printf( "vp->vmm_seg = 0\n" );
1334: }
1335:
1336: /*
1337: * update the terminal to match vtactive
1338: */
1339: updterminal(index)
1340: int index;
1341: {
1342: updscreen(index);
1343: updleds();
1344: }
1345:
1346: #undef si
1347: asmdump( cs, ds, es, di, si, bp, sp, bx, dx, cx, i, ip, ax )
1348: int cs, ds, es, di, si, bp, sp, bx, dx, cx, i, ip, ax;
1349: {
1350: if( vt_verbose < 2 )
1351: return;
1352:
1353: printf( "asmdump %d: es %x, ds %x, cs:ip %x:%x\n", i, es, ds, cs, ip );
1354: printf( " ax %x, bx %x, cx %x, dx %x\n", ax, bx, cx, dx );
1355: printf( " di %x, si %x, bp %x, sp %d\n", di, si, bp, sp );
1356: #if USING_RS232
1357: if( vt_verbose > 2 )
1358: getchar();
1359: #endif
1360: }
1361:
1362: vtdataprint( vp )
1363: register VTDATA *vp;
1364: {
1365: if( vt_verbose < 2 )
1366: return;
1367:
1368: printf( "VTDATA: @%x, esc %x, func %x()\n",
1369: vp, vp->vmm_esc, vp->vmm_func );
1370: printf( " hw: port %x, seg %x, off %x\n",
1371: vp->vmm_port, vp->vmm_vseg, vp->vmm_voff );
1372: printf( " memory: size %x, seg %x, off %x\n",
1373: 0/*vp->vmm_size*/, vp->vmm_mseg, vp->vmm_moff );
1374: printf( " cursor: seg %x, off %x, visible %d\n",
1375: vp->vmm_seg, vp->vmm_off, !vp->vmm_invis );
1376: printf( " row %d, col %d = offset %d.\n",
1377: vp->vmm_rowl, vp->vmm_col, vp->vmm_pos );
1378: printf( " saved row %d, col %d\n",
1379: vp->vmm_srow, vp->vmm_scol );
1380: printf( " screen: visible %d, attr %x, wrap %d, slow %d\n",
1381: vp->vmm_visible, vp->vmm_attr, vp->vmm_wrap, vp->vmm_slow );
1382: printf( " row base %d, end %d, limit %d\n",
1383: vp->vmm_brow, vp->vmm_erow, vp->vmm_lrow );
1384: printf( " row initial base %d, initial end %d\n",
1385: vp->vmm_ibrow, vp->vmm_ierow );
1386: #if USING_RS232
1387: if( vt_verbose > 2 )
1388: getchar();
1389: #endif
1390: }
1391:
1392: FFCOPY( src_off, src_seg, dst_off, dst_seg, count )
1393: {
1394: register i;
1395:
1396: #if 0
1397: i = ffcopy( src_off, src_seg, dst_off, dst_seg, count );
1398: #else
1399: for( i = 0; i < count; i += 2 ) {
1400: register word = ffword( src_off, src_seg );
1401: sfword( dst_off, dst_seg, word );
1402: src_off += 2;
1403: dst_off += 2;
1404: }
1405: #endif
1406: return i;
1407: }
1408:
1409: /*
1410: * Given a function key number (e.g. vt0),
1411: * return the corresponding minor device number.
1412: *
1413: * Assume valid key number (VTKEY(fnum) is true) by the time we get here.
1414: */
1415: int
1416: vtkey_to_dev(fnum)
1417: int fnum;
1418: {
1419: if (fnum >=vt0 && fnum <= vt15)
1420: return fnum-vt0+1;
1421: if (fnum >=color0 && fnum <= color15)
1422: return (fnum-color0)|(VT_PHYSICAL|VT_HW_COLOR);
1423: if (fnum >=mono0 && fnum <= mono15)
1424: return (fnum-mono0)|(VT_PHYSICAL|VT_HW_MONO);
1425: printf("vtkey_to_dev(%d)! ", fnum);
1426: return 0;
1427: }
1428: /* End of vtkb.c */
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.