paradis@encore.UUCP (Jim Paradis) (01/07/88)
Well, friends, here's what you've all been waiting for... a serial TTY driver for MINIX! This is a rewrite from the ground up, not a modification of the existing driver, although this new driver is pretty much plug-compatible with the old. (You should still read the installation instructions in README before proceeding, though). As this went to press (silicon?) there was one problem just discovered: since the serial portion of this driver is interrupt-driven, if you're running any code that does a lock() for any appreciable length of time, you CAN drop characters. I discovered this by accident one day when I tried to do an 'lpr' in the background while running a terminal session. That also explained why I was sometimes losing characters on emacs sessions and the like -- if I made a mistake, emacs would beep at me. And the beep() routine disables interrupts for the duration of the beep! If anyone out there has some answers, I'd appreciate knowing them. Enjoy! Any problems/suggestions, send email, though I don't know how quickly I'll be able to respond... +----------------+ Jim Paradis linus--+ +--+-------------+ | Encore Computer Corp. necntc--| | | E N C O R E | | 257 Cedar Hill St. ihnp4--+-encore!paradis | +-------------+--+ Marlboro MA 01752 decvax--| +----------------+ (617) 460-0500 talcott--+ But our sales would PLUMMET!! ------------cut here--------------------unpack with /bin/sh-------------- echo x - MANIFEST gres '^X' '' > MANIFEST << '/' XAfter unpacking your TTY kit, you should have the following files: X X-rw-rw-rw- 1 root 14292 Jan 3 13:43 README X-rw-r--r-- 1 root 32926 Jan 3 13:30 console.c X-rw-rw-rw- 1 root 4590 Jan 3 13:30 klib88.newstf X-rw-rw-rw- 1 root 284 Jan 3 13:30 mansi.tc X-rw-rw-rw- 1 root 791 Jan 3 13:41 mpx88.newstf X-rw-r--r-- 1 root 14761 Jan 3 13:30 rs232.c X-rw-r--r-- 1 root 2081 Jan 3 13:30 t.c X-rw-r--r-- 1 root 34687 Jan 3 13:30 tty.c X-rw-r--r-- 1 root 3321 Jan 3 13:30 tty.h X XFollowing is a brief description of each file: X XREADME Read this file before doing anything else! X Xtty.c The device-independent portion of the TTY driver X Xtty.h Definitions used by the various components of the TTY X driver. X Xconsole.c The device-specific portion of the driver for the IBM-PC X keyboard and display adapter. This SHOULD work with most X CGA-compatible and mono display adapters, although it's only X received extensive testing with a CGA-clone. X Xrs232.c The device-specific portion of the driver for serial X devices. X Xklib88.newstf New routines to add to the "klib88.s" file. X Xmpx88.newstf New routines to add to the "mpx88.s" file. X Xmansi.tc A termcap entry corresponding to the ANSI capabilities X of the console driver. X Xt.c Source for one of the world's cheapest & dirtiest terminal X programs. What can I say? It works! / echo x - README gres '^X' '' > README << '/' XBefore doing anything else, I suggest consulting the file named XMANIFEST to verify that you have received all the files which Xcomprise this kit. After that, I suggest that you read this file Xfrom top to bottom before you attempt to install your driver. X XThis kit contains Jim Paradis' new TTY driver for MINIX. Although Xpretty much plug-compatible with the stock TTY driver, my driver Xis a complete rewrite from the ground up with the exception of some Xkeyboard-handling stuff and a few other minor things which I lifted Xfrom the original TTY driver (Acknowledgements and mucho thanks to XAndy Tanenbaum!!). Among the features supported by the new driver are: X X - Serial TTY support for up to four serial lines X (As shipped, the driver is configured for one X serial line, but you only have to change one X #define to get the number you want, up to four. X Each serial line chews up some memory for buffers X and tables, so you should only configure those X you need) X X - On the PC console, long lines now wrap instead of X being truncated at the edge of the screen. X X - Typing the KILL character now causes the line to X be erased. X X - Word-erase and redisplay are now supported. X X - Control characters are echoed as "^X" X X - This driver supports a sufficient subset of ANSI X terminal escape sequences to satisfy most editors X (Including ELLE) X X - Debugging functions (e.g. proc dump, memory dump) X are now done by hitting CTRL-ALT-Fkey rather than X just Fkey. This frees the Fkeys to be used by other X things. X XFunction Keys: X X This driver supports the function keys F1 through F12 in Xfour different ways. Just hitting a function key causes the following Xescape sequences to be generated: X X F1 Esc OP F5 Esc OT F9 Esc OX X F2 Esc OQ F6 Esc OU F10 Esc OY X F3 Esc OR F7 Esc OV F11 Esc OZ X F4 Esc OS F8 Esc OW F12 Esc O[ X XHolding down CTRL while hitting a function key causes the following Xescape sequences to be generated: X X F1 Esc QP F5 Esc QT F9 Esc QX X F2 Esc QQ F6 Esc QU F10 Esc QY X F3 Esc QR F7 Esc QV F11 Esc QZ X F4 Esc QS F8 Esc QW F12 Esc Q[ X XIn addition, this driver has hooks for implementing user-definable Xfunction keys, where up to sixteen characters can be stored for each Xkey. Holding down ALT while hitting a function key causes the associated Xtext to be transmitted as though you had typed it at the console keyboard. XUntil I get around to re-doing ioctl, though, there isn't any way to Xtransmit function key text to the driver to be stored. If you wish, though, Xyou CAN hard-code function-key text into the driver, and I have provided Xan example such that ALT-F1 is bound to the string "ls\r". If you look at Xthe beginning of con_init() in console.c, you'll see how it's done. X XFinally, hitting CTRL-ALT-Fkey causes various debug functions (e.g. proc_dmp()) Xto be invoked, much as they are by hitting just the function key in the old Xtty driver. X XCharacter Defaults X X I use the following control characters as defaults Xin this driver. They are different than the MINIX defaults. XYou may wish to change them before installing the driver. They Xare defined in the first few lines of tty.h: X X interrupt ^C X quit ^\ X erase ^H X kill ^U X word-erase ^W X redisplay ^R X XOFF (suspend) ^S X XON (resume) ^Q X EOT ^D X literal-next \ X X XInstalling the driver: X X To install this driver, you must do the following: X X (1) Move the following files from this distribution into X your kernel source directory: X X tty.h X tty.c (you may want to back up the old tty.c first!) X console.c X rs232.c X X (2) Edit h/const.h and bump NR_TASKS from 8 to 9. X X (3) Edit h/com.h and add the following line: X X #define TTY_ASYNC -9 X X (4) Edit kernel/const.h and add the following lines: X X #define SER1_VECTOR 12 X #define SER2_VECTOR 11 X X (5) Edit kernel/main.c and add the following lines among X all the other set_vec calls: X X set_vec(SER1_VECTOR, ser1_int, base_click); X set_vec(SER2_VECTOR, ser2_int, base_click); X X Also, add the following line to the declaration X section: X X extern int ser1_int(), ser2_int(); X X (6) Edit kernel/dmp.c and add the entry "TTASYN" to the X front of the "naymes" array. X X (7) Edit kernel/table.c and change the initialization of the X "task" array to the following: X X int (*task[NR_TASKS+INIT_PROC_NR+1])() = { X tty_async_task, printer_task, tty_task, X winchester_task, floppy_task, X mem_task, clock_task, sys_task, 0, 0, 0, 0 X }; X X Also, add the line: X X extern int tty_async_task(); X X to the declaration section. X X X (8) Edit your kernel makefile and add console.s and rs232.s to X the definition of "obj". Also, add the following dependencies: X X tty.s: const.h type.h $h/const.h $h/type.h X tty.s: $h/callnr.h X tty.s: $h/com.h X tty.s: $h/error.h X tty.s: $h/sgtty.h X tty.s: $h/signal.h X tty.s: glo.h X tty.s: proc.h X tty.s: tty.h X X console.s: const.h X console.s: $h/type.h X console.s: $h/const.h X console.s: $h/com.h X X rs232.s: const.h X rs232.s: tty.h X rs232.s: $h/const.h X rs232.s: $h/type.h X rs232.s: $h/com.h X X (9) If you have more than one serial line that you wish to X support, then edit tty.h and change the line X X #define NUM_SERIAL_DEV 1 X X to the number of serial devices that you wish to have. Note X that if you have only one serial device connected to the system X but it's configured as COM2:, COM3:, or COM4:, then you still X will have to configure the driver for two, three, or four devices X respectively. Of course, it might be preferable to just reconfigure X the device itself as COM1:. X X (10) Incorporate the routines in the file "klib88.newstf" into your X klib88.s file. Delete the routine _vid_copy: as well as all references X to the symbols "vid_copy" and "vid_mask". Similarly, incorporate the X routines in the file "mpx88.newstf" into your mpx88.s file. X X (11) Build your kernel! X X (12) If you have a termcap file, you may wish to incorporate X the contents of the file "mansi.tc" into it. It defines X a terminal type "mansi", which corresponds to the ANSI support X provided for the console driver. X X Once your TTY driver is installed, you'll probably want to try X it out. The file "t.c" contains the source for a VERY cheap X terminal program (I knocked it together in about 10 minutes). X Cheap though it may be, I find that if I'm talking to a UNIX X system and I've uploaded my "mansi" termcap entry to that system, X then it's more than adequate for interactive use. It doesn't do X file transfers at all, but I'll try porting some other program X for that. X XCheap vs. Good CGAs. X X You may notice that the file console.c contains some code that is Xconditionally compiled on the parameter CHEAP_CGA. In this case, a cheap XCGA is one that doesn't dual-port the video RAM properly, such that writing Xto video RAM at any time other than during blanking intervals causes "snow" to Xappear on the screen. This driver comes with CHEAP_CGA defined so that Xeveryone can use it right out of the box. However, if you have an EGA or a Xgood CGA that has properly dual-ported RAM, then you will want to #undef this Xparameter (performance should improve significantly). X X XTechnical notes on the driver: X X Unfortunately, the present implementation of the driver only Xallows for a single hard-coded baud rate on the serial lines (see Xser_init() in rs232.c). The reason for this is that the present Xstructure of the ioctl() system call is too limited to support full-blown Xioctls like in v7 or sysV. Therefore, to keep with the design criterion Xthat the new driver be as plug-compatible as possible with the old, XI have omitted the feature of line control. Don't worry, it will exist Xin a future version of the driver (heck, I won't be able to port half the Xsoftware I want to port if it doesn't exist!). X X In order to cut down on interrupt and TTY_CHAR_INT message traffic Xwhen characters come in thick and fast on the serial line, the serial Xinterrupt handler waits for a short time after it receives a character to Xsee if there is another character immediately following. There is a X#define near the beginning of rs232.c of a parameter called MAX_CHARS_PER_INT. XThis is the maximum number of characters that will be received this way on Xa single interrupt. If this number is too low, then interrupt traffic will Xbe needlessly increased. If this number is too HIGH, then input will tend Xto come in in "bursts" rather than smoothly. This is especially noticeable Xif you're doing, say, a "tip" over that serial line. I found the supplied Xvalue of 3 to be a nice compromise. I may make this a special ioctl Xvparameter in the future (so that you can set it higher for non-interactive Xinput. This would be especially useful if you're doing "packetized" I/O Xsuch as SL/IP. Setting this value to the packet size would result in taking Xonly one interrupt per packet!) X X Also, there's a bit of magick in the "additional character" Xhandling itself. Note the following line from rs232.c: X X for(latency = 0; latency < (rs->rs_baud << 1); latency++) { X XThe parameter "rs->rs_baud" is a counter divisor that the serial Xchip uses to determine the baud rate. By empirical observation, I Xdetermined that, on a 12-MHz AT, looping for TWICE this number of Xiterations while waiting for another character yielded the best Xresults. However, if you loop TOO many times, you'll lose, as you Xmay end up waiting around for nothing (i.e. the timeout will be too Xlong) and you may actually end up DROPPING characters as a result. XTherefore, if you're running a slow machine (i.e. a 4.77MHz true-blue PC), Xyou may want to omit the "<< 1". Alternately, if you're running a 20-MHz X80386 machine, you may want to use "<< 2" instead. X XADDING YOUR OWN DEVICES: X X I wrote this driver intending it to be relatively easy to add Xadditional types of devices to it, and I've kept the interface between Xthe "tty-driver" portion of the driver and the "device-driver" portion Xas clean as possible. In fact, the entire interface consists of seven Xroutine entry points and one message protocol. Following is a brief Xdiscussion of the entry points: X Xvoid (*tt_dev_init)(minor, devnum); XThis routine is called when the TTY driver is initialized, and it Xhas two functions: first, it performs any device-specific Xinitializations necessary for the particular device, and second, it Xassociates a minor device number with a particular hardware device. XThe "devnum" parameter is provided for those device-specific components Xwhich support multiple devices. For example, the rs232 driver supplied Xsupports up to four devices. To the rs232 driver, these devices are numbered X0 thru 3. However, the TTY driver knows them as minor devices 1 thru 4, Xsince 0 is already taken by the console. Hence, the rs232 driver has Xto take care of the mapping. X Xvoid (*tt_dev_putc)(minor, c); XThis routine outputs a character to the specified device. Note that upon Xreturning from this routine it is not guaranteed that the character has Xactually made it out to the device; on a slow device it may well be the Xcase that we queue up characters to be written, and the actual character Xwrites take place upon receipt of a "device ready" interrupt. This is of Xno consequence to the TTY driver; when it wants to make SURE that characters Xhave gotten out to where they're going, it will call the flush-output Xroutine X Xvoid (*tt_dev_ioctl)(minor, type, ctlp); XThis routine handles device-specific ioctl requests. Since this rev of Xthe driver doesn't support such an animal, this interface remains unused Xas of this writing. However, to prevent a plethora of implementers from Xdefining their own interfaces, I will SUGGEST that this routine take (1) Xa minor device number, (2) an integer ioctl type, and (3) a pointer to a Xstructure containing the ioctl request to be performed. The size and Xformat of the structure may very well be dependent on the request type. XWatch this space for further details. X Xvoid (*tt_dev_fcon)(minor); XThis routine is called when the TTY driver's internal buffers are starting Xto fill up. If there is any way to flow-control a terminal (e.g. sending XXOFF) this routine should perform that operation on the specified device. XSome devices (such as the console) do not have nor require flow-control Xcapabilities. The present driver does very little with this flag. X Xvoid (*tt_dev_fcoff)(minor); XThis routine is called when the TTY driver's internal buffers drain after Xan "fcon" has been performed. It should re-enable the device (e.g. send XON) X Xvoid (*tt_dev_oflush)(minor); XThis routine flushes all output pending on the specified device. Upon Xreturning from this routine, one can be guaranteed that all characters Xwritten to the device have indeed been written. X Xint (*tt_dev_bufin)(minor, buf, maxchars); XThis routine implements the receiving end of the character-input protocol. XWhenever a character(s) is/are received by the device driver, the device Xdriver should buffer the character(s), construct a TTY_CHAR_INT message Xcontaining the minor device number of the serial line in the TTY_LINE Xfield, and send this message to the TTY task. The TTY task, in turn Xwill then call this routine to get any characters buffered up to this Xpoint. The "buf" parameter is a pointer to a buffer to receive the Xbuffered characters, and the "maxchars" parameter is the maximum number Xof characters to receive. This routine should return the number of Xcharacters actually placed in the buffer. X XOne small note about the handling of TTY_CHAR_INT messages: you may Xnotice in tty_task() that whenever we take a TTY_CHAR_INT message, we Xactually check ALL configured tty's for input, starting with the one Xthat sent the message. The reason for this is that if we have a number Xof devices configured, each clamoring for attention, it's entirely Xpossible that a message will get dropped. The reason for this is that Xsuch messages are sent from the TTY driver to itself, and if the TTY Xdriver is busy handling another message at the time then the message Xwaiting to get sent goes into the "pending-message" field of TTY's Xproc structure. If we take ANOTHER interrupt during this period, then Xthis pending message might get overwritten with the next one that gets Xsent to the TTY. Essentially the problem is that the message queue from XTTY to itself is only one message long, and we may very well try to send Xmessages faster than TTY can receive them. The approach of checking Xeach TTY device whenever we get a TTY_CHAR_INT message is MUCH easier Xthan implementing a full-blown queueing mechanism for messages, and the Xcheck itself is fairly cheap. X / echo x - console.c gres '^X' '' > console.c << '/' X/************************************************************************** X * File: console.c X * Creation date: 9/19/87 X * X * All original code is Copyright 1988, James R. Paradis. Permission X * granted to copy and redistribute for educational and non-commercial X * use. Commercial use requires permission of copyright holder. X * X * This file contains the low-level console driver support routines X * for the MINIX tty driver. These routines interface directly to X * the IBM-PC hardware. This version of the driver supports the X * IBM Color Graphics Adaptor. Support for other display adapters X * may be added later. X * X * Entry points: X * X * con_init(minor, dev_num) X * con_putc(minor, char) X * con_ioctl(minor, func, addr) X * con_fc(minor) X * con_nofc(minor) X * con_oflush(minor) X * con_bufin(minor) X * X * Internal routines: X * scr_set_cursor(row, col) X * scr_forward_scroll(nlines) X * scr_flush_output(option) X * scr_shortescape(); X * scr_longescape() X * ... X * X **************************************************************************/ X X#include "../h/type.h" X#include "../h/const.h" X#include "../h/com.h" X#include "const.h" X X/* If we're using a CGA that produces snow if we access video RAM X * outside the blanking interval, then we define CHEAP_CGA. Otherwise, X * we can pull this define for faster performance X */ X X#define CHEAP_CGA X X/* Type declarations and global data */ X X#ifdef CHEAP_CGA X X/* An entry in the output queue. Since we can only write characters out X * during the blanking interval, we queue up characters to be written until X * we can write them out. We implement this as a circular queue. X */ Xtypedef struct { X int address; X union { X struct { X char ch; X char attrib; X } c; X int i; X } u; X} OQUE_ENTRY; X X#define CH u.c.ch X#define ATTRIB u.c.attrib X#define INTVAL u.i X X/* Size of the output queue. We don't want to make this TOO big, X * or else the output may start appearing in bursts rather than X * smoothly. This number may be one to experiment with. X */ X#define NUM_OQUE_ENTRIES 80 X XPRIVATE OQUE_ENTRY oque[NUM_OQUE_ENTRIES]; XPRIVATE int oque_first; XPRIVATE int oque_last; XPRIVATE int oque_size; X X#endif CHEAP_CGA X X/* Current cursor position. We keep track of this so we know when to X * do things like wrapping and the like. X */ X#define NROWS 25 X#define NCOLS 80 X XPRIVATE int crow, ccol; X X/* Character attribute definitions and current character attribute byte. */ X#define A_CHBLUE 0x01 X#define A_CHGREEN 0x02 X#define A_CHRED 0x04 X#define A_CHWHITE 0x07 X#define A_INTENSE 0x08 X#define A_BGBLUE 0x10 X#define A_BGGREEN 0x20 X#define A_BGRED 0x40 X#define A_BGWHITE 0x70 X#define A_BLINK 0x80 X XPRIVATE char attrib; X X/* Hardware port and register definitions */ XPRIVATE int ADDR_PORT; XPRIVATE int DATA_PORT; XPRIVATE int MODE_PORT; XPRIVATE int COLOR_PORT; XPRIVATE int STATUS_PORT; X X#define CGA_ADDR_PORT 0x3d4 X#define CGA_DATA_PORT 0x3d5 X#define CGA_MODE_PORT 0x3d8 X#define CGA_COLOR_PORT 0x3d9 X#define CGA_STATUS_PORT 0x3da X X#define MONO_ADDR_PORT 0x3b4 X#define MONO_DATA_PORT 0x3b5 X#define MONO_MODE_PORT 0x3b8 X#define MONO_COLOR_PORT 0x3b9 X#define MONO_STATUS_PORT 0x3ba X X#define START_HI 12 X#define START_LO 13 X#define CURSOR_HI 14 X#define CURSOR_LO 15 X X#define BLANKING_ON 1 X X#define TIMER2 0x42 X#define TIMER3 0x43 X#define KEYBD 0x60 X#define PORT_B 0x61 X#define KBIT 0x80 X#define B_TIME 0x2000 X X#define AT_SIGN 0220 /* Special value for ^@ */ X X/* Current start and cursor values */ XPRIVATE int MAX_START; XPRIVATE int SEG_MASK; XPRIVATE int VIDEO_SEG; X X#define CGA_MAX_START 0x4000 X#define CGA_SEG_MASK 0x3fff X#define CGA_VIDEO_SEG 0xb800 X X#define MONO_MAX_START 0x1000; X#define MONO_SEG_MASK 0x0fff; X#define MONO_VIDEO_SEG 0xb000 X XPRIVATE int cur_start; XPRIVATE int cur_cursor; X Xextern int color; X X X/* Table of recommended values to send to the 6845 on initialization */ XPRIVATE int m6845_init_table[] = { 0x71, 0x50, 0x5a, 0x0a, 0x1f, X 0x06, 0x19, 0x1c, 0x02, 0x07, X 0x00, 0x1f, 0, 0, 0, 0 }; X X/* State of escape processing. This version of the tty driver will X * support a subset of the ANSI escape sequences. These sequences X * are of two general types: X * ESC [ num ; num ; ... X X * ESC X X * Where "X" is any non-numeric character. X */ XPRIVATE int escape_state = 0; X X/* Escape state defines */ X#define ESC_INESCAPE 1 /* In escape sequence */ X#define ESC_BRACKET 2 /* Bracket seen */ X#define ESC_FIRSTPARAM 4 /* First param seen */ X X/* Number of escape parameters seen */ XPRIVATE int num_esc_params = 0; XPRIVATE int esc_params[16]; X X/* A state variable to control wrapping at the end of a line. When we X * write a character INTO the last column of the screen, we set this X * variable to 1 so that we know that the next write should wrap the X * screen. Any operation that moves the cursor FROM column 80 should X * definitely CLEAR this variable! X */ XPRIVATE int wrap_needed; XPRIVATE int wrap_enabled = 1; X XPRIVATE int cint_sent; X X/* Definitions of special characters */ X#define CHAR_BELL 7 X#define CHAR_BS 8 X#define CHAR_CR 13 X#define CHAR_LF 10 X#define CHAR_ESC 27 X#define CHAR_TAB 9 X X/* Tab table. For now, tabs will be hard-coded at 8; however, we may X * eventually wish to make this adjustable by a special ioctl. For that X * reason, we're using a table-driven algorithm. The table is X * terminated with -1. X */ X XPRIVATE int tab_table[] = { 9, 17, 25, 33, 41, 49, 57, 65, 73, 80, -1 }; X X/* TRUE if flow control is enabled. With FC enabled, inputs are X * disallowed. X */ XPRIVATE int fc_on; X XPUBLIC scan_code; /* scan code for '=' saved by bootstrap */ X X X/* Scan codes to ASCII for unshifted keys */ XPRIVATE char unPC[] = { X 0,033,'1','2','3','4','5','6', '7','8','9','0','-','=','\b','\t', X 'q','w','e','r','t','y','u','i', 'o','p','[',']',015,0202,'a','s', X 'd','f','g','h','j','k','l',';', 047,0140,0200,0134,'z','x','c','v', X 'b','n','m',',','.','/',0201,'*', 0203,' ',0204,0241,0242,0243,0244,0245, X 0246,0247,0250,0251,0252,0205,0210,0267, 0270,0271,0211,0264,0265,0266,0214 X,0261,0262,0263,'0',0177 X}; X X/* Scan codes to ASCII for shifted keys */ XPRIVATE char shPC[] = { X 0,033,'!','@','#','$','%','^', '&','*','(',')','_','+','\b','\t', X 'Q','W','E','R','T','Y','U','I', 'O','P','{','}',015,0202,'A','S', X 'D','F','G','H','J','K','L',':', 042,'~',0200,'|','Z','X','C','V', X 'B','N','M','<','>','?',0201,'*', 0203,' ',0204,0221,0222,0223,0224,0225, X 0226,0227,0230,0231,0232,0204,0213,'7', '8','9',0211,'4','5','6',0214,'1', X '2','3','0','.' X}; X X X/* Scan codes to ASCII for Olivetti M24 for unshifted keys. */ XPRIVATE char unm24[] = { X 0,033,'1','2','3','4','5','6', '7','8','9','0','-','^','\b','\t', X 'q','w','e','r','t','y','u','i', 'o','p','@','[','\r',0202,'a','s', X 'd','f','g','h','j','k','l',';', ':',']',0200,'\\','z','x','c','v', X 'b','n','m',',','.','/',0201,'*', 0203,' ',0204,0241,0242,0243,0244,0245, X 0246,0247,0250,0251,0252,023,0210,0267,0270,0271,0211,0264,0265,0266,0214,0261, X0262,0263,'0','.',' ',014,0212,'\r', 0264,0262,0266,0270,032,0213,' ','/', X 0253,0254,0255,0256,0257,0215,0216,0217 X}; X X/* Scan codes to ASCII for Olivetti M24 for shifted keys. */ XPRIVATE char m24[] = { X 0,033,'!','"','#','$','%','&', 047,'(',')','_','=','~','\b','\t', X 'Q','W','E','R' ,'T','Y','U','I', 'O','P',0140,'{','\r',0202,'A','S', X 'D','F','G','H','J','K','L','+', '*','}',0200,'|','Z','X','C','V', X 'B','N','M','<','>','?',0201,'*', 0203,' ',0204,0221,0222,0223,0224,0225, X 0226,0227,0230,0231,0232,0270,023,'7', '8','9',0211,'4','5','6',0214,'1', X '2','3',0207,0177,0271,014,0272,'\r', '\b','\n','\f',036,032,0273,0274,'/', X 0233,0234,0235,0236,0237,0275,0276,0277 X}; X X/* Function key scan codes */ X#define IS_FKEY(n) ((n >= 0x3b && n <= 0x44) || (n >= 0x57 && n <= 0x58)) X#define SCAN_CODE_TO_FKEY(n) ((n < 0x45) ? (n - 58) : (n - 76)) X X/* Misc. scan codes */ X#define DEL_CODE (char)83 X#define TOP_ROW 14 X X/* These point to whichever table to actually use */ XPRIVATE char * unsh; XPRIVATE char * sh; X X/* Variables to hold the shift-state of the keyboard */ XPRIVATE int shift1, shift2, control, alt, capslock, numlock; XPRIVATE int caps_off = 1; XPRIVATE int num_off = 1; X X/* This message is used to send data to the upper layer */ XPRIVATE message keybd_mess; X X/* set in main.c to 0 if mono, 1 if color. */ XPUBLIC int color; X X/* A small, internal, circular buffer for received characters */ X#define CIBUFSZ 0x80 X#define CIBUFSZMASK 0x7f XPRIVATE char con_ibuf[CIBUFSZ]; XPRIVATE int con_first, con_last, con_size; XPRIVATE char con_ilock; X X#define NUM_FKEYS 12 X#define FKEY_TEXT_SIZE 16 X X/* Definitions for programmable keys (ALT-Fx) */ Xtypedef struct { X int fk_nchars; X char fk_text[FKEY_TEXT_SIZE]; X} FKEY; X XPRIVATE FKEY fkey_defs[NUM_FKEYS]; X X/************************************************************************ X * con_init(minor, devnum) X *************************************************************************/ XPUBLIC con_init(minor, devnum) Xint minor; Xint devnum; X{ X int i; X X /* Set up function key definitions. For now, to test it out, X * set up fkey 1 as 'ls', fkey's 2-12 are undefined. X */ X fkey_defs[0].fk_nchars = 3; X fkey_defs[0].fk_text[0] = 'l'; X fkey_defs[0].fk_text[1] = 's'; X fkey_defs[0].fk_text[2] = '\r'; X X for(i = 1; i < NUM_FKEYS; i++) { X fkey_defs[i].fk_nchars = 0; X } X X /* Determine whether we have a color or mono display attached, X * and set up accordingly. X */ X if(color) { X MAX_START = CGA_MAX_START; X SEG_MASK = CGA_SEG_MASK; X VIDEO_SEG = CGA_VIDEO_SEG; X ADDR_PORT = CGA_ADDR_PORT; X DATA_PORT = CGA_DATA_PORT; X MODE_PORT = CGA_MODE_PORT; X COLOR_PORT = CGA_COLOR_PORT; X STATUS_PORT = CGA_STATUS_PORT; X } X else { X MAX_START = MONO_MAX_START; X SEG_MASK = MONO_SEG_MASK; X VIDEO_SEG = MONO_VIDEO_SEG; X ADDR_PORT = MONO_ADDR_PORT; X DATA_PORT = MONO_DATA_PORT; X MODE_PORT = MONO_MODE_PORT; X COLOR_PORT = MONO_COLOR_PORT; X STATUS_PORT = MONO_STATUS_PORT; X } X X /* Initialize the display adapter. First, disable video while we X * muck about... X */ X port_out(MODE_PORT, 0x21); X X /* Initialize all of video memory. */ X vidfill(VIDEO_SEG, 0, MAX_START >> 1, 0x0700); X X /* Initialize the 6845 */ X for(i = 10; i < 16; i++) { X port_out(ADDR_PORT, i); X port_out(DATA_PORT, m6845_init_table[i]); X } X X /* Select video mode, re-enable video output, no fancy colors */ X port_out(MODE_PORT, 0x29); X port_out(COLOR_PORT, 0); X X#ifdef CHEAP_CGA X /* Initialize output queue */ X oque_first = 0; X oque_last = 0; X oque_size = 0; X#endif CHEAP_CGA X X /* Initialize global data */ X crow = 0; X ccol = 0; X attrib = A_CHWHITE; X cur_start = 0; X cur_cursor = 0; X escape_state = 0; X X /* Flow control is OFF */ X fc_on = 0; X X /* Initialize input queue */ X con_first = con_last = con_size = 0; X con_ilock = 0; X X /* Select scan-code table to use. If the scan code for '=' X * is 12, use the Olivetti table, otherwise use the PC table X */ X if(scan_code == 12) { X unsh = unm24; X sh = m24; X } X else { X unsh = unPC; X sh = shPC; X } X X cint_sent = 0; X} X X X/************************************************************************ X * con_putc(minor, ch) X *************************************************************************/ XPUBLIC con_putc(minor, ch) Xint minor; Xchar ch; X{ X int i; X X X /* If we are handling an escape sequence, then X * continue handling it. X */ X if(escape_state & ESC_INESCAPE) { X if(escape_state & ESC_BRACKET) { X /* We've already seen a bracket. If the char is X * a semicolon, go to the next param. If it's a X * digit, add it in. Otherwise, execute the X * sequence. X */ X if(ch == ';') { X esc_params[++num_esc_params] = 0; X } X else if((ch >= '0') && (ch <= '9')) { X esc_params[num_esc_params] *= 10; X esc_params[num_esc_params] += (int)(ch - '0'); X } X else { X scr_longescape(ch); X escape_state = 0; X } X } X else if(ch == '[') { X escape_state |= ESC_BRACKET; X } X else { X scr_shortescape(ch); X escape_state = 0; X } X return; X } X X /* Handle special characters. If it's not a special char, X * then drop it on the queue and flush it out if it's not X * too much trouble. Also, handle line wrapping and screen X * scrolling as necessary! X */ X switch(ch) { X case CHAR_ESC: X /* Escape. Put us into escape-processing mode */ X escape_state |= ESC_INESCAPE; X num_esc_params = 0; X esc_params[0] = 0; X break; X X case CHAR_TAB: X /* Tab. Go thru the tab table until we find a X * tab stop after our current position. If we find X * it, set our cursor there. X */ X for(i = 0; tab_table[i] > 0; i++) { X if (tab_table[i] > (ccol + 1)) { X scr_set_cursor(crow, (tab_table[i] - 1)); X break; X } X } X break; X X case CHAR_BS: X /* Backspace. If we're not at the left edge of the X * screen, then back our cursor up one notch. X */ X if(ccol > 0) { X scr_set_cursor(crow, ccol - 1); X } X break; X X case CHAR_CR: X /* Carriage return. Put the cursor at the beginning X * of the line X */ X scr_set_cursor(crow, 0); X break; X X case CHAR_LF: X /* Linefeed. If we're on the bottom row of the screen, X * we have to scroll the screen down a line first. In X * either case, position our cursor on the next line X * scr_forward_scroll automatically repositions the cursor X * to reflect the fact that the screen scrolled up... X */ X if(crow == (NROWS - 1)) { X scr_forward_scroll(1); X } X scr_set_cursor(crow+1, ccol); X break; X X case CHAR_BELL: X beep(0x0533); X break; X X default: X /* This is merely a character to be written out. X * If it's a non-printing character, ignore it. The X * high-level TTY driver should have handled this X * already. Otherwise, check our position. If we X * are at the end of the line, then we have to wrap X * to the beginning of the next line. This may, in turn X * require that we scroll the screen. X */ X if(ch > CHAR_ESC) { X if((ccol == NCOLS - 1) && wrap_needed && wrap_enabled) { X if(crow == NROWS - 1) { X scr_forward_scroll(1); X } X scr_set_cursor(crow+1, 0); X } X X#ifdef CHEAP_CGA X /* Now we're at the spot where we'd like the X * character to go. Make sure there's a clear X * spot in the queue for us to drop a character X * into... X */ X while(oque_size == NUM_OQUE_ENTRIES) { X scr_flush_output(2); X } X X oque[oque_last].address = cur_start + X ( ((crow * NCOLS) + ccol) << 1); X oque[oque_last].ATTRIB = attrib; X oque[oque_last].CH = ch; X oque_last = (oque_last + 1) % NUM_OQUE_ENTRIES; X oque_size++; X X /* Flush whatever we can... */ X scr_flush_output(1); X X#else X put_byte(VIDEO_SEG, X (int)(cur_start + (((crow * NCOLS) + ccol) << 1)), X (int)ch); X put_byte(VIDEO_SEG, X (int)(cur_start + (((crow * NCOLS) + ccol) << 1)) + 1, X (int)attrib); X#endif X X /* Set our cursor to the next position */ X if(ccol == NCOLS - 1) { X /* If we just wrote INTO the last column, then X * set wrap_needed. We don't need to reposition X * the cursor at this point... X */ X wrap_needed = 1; X } X else { X scr_set_cursor(crow, ccol+1); X } X } X break; X } X X} X X/************************************************************************ X * con_ioctl(minor, func, addr) X *************************************************************************/ XPUBLIC con_ioctl(minor, func, addr) Xint minor; Xint func; Xchar * addr; X{ X /* No ioctls supported at the moment... */ X return; X} X X X/************************************************************************ X * con_fc(minor) X *************************************************************************/ XPUBLIC con_fc(minor) Xint minor; X{ X fc_on = 1; X} X X X/************************************************************************ X * con_nofc(minor) X *************************************************************************/ XPUBLIC con_nofc(minor) Xint minor; X{ X fc_on = 0; X} X X X/************************************************************************ X * con_oflush(minor) X *************************************************************************/ XPUBLIC con_oflush(minor) Xint minor; X{ X X#ifdef CHEAP_CGA X scr_flush_output(3); X#endif CHEAP_CGA X} X X X/************************************************************************ X * scr_set_cursor(row, col) X *************************************************************************/ XPRIVATE scr_set_cursor(row, col) Xint row; Xint col; X{ X X row = (row < 0) ? 0 : row; X crow = (row > NROWS) ? NROWS : row; X col = (col < 0) ? 0 : col; X ccol = (col > NCOLS) ? NCOLS : col; X cur_cursor = (cur_start + (((crow * NCOLS) + ccol) << 1)); /* & SEG_MASK */ X X port_out(ADDR_PORT, CURSOR_LO); X port_out(DATA_PORT, (int)(cur_cursor >> 1) & 0xff); X port_out(ADDR_PORT, CURSOR_HI); X port_out(DATA_PORT, (int)(((cur_cursor >> 1) & 0xff00) >> 8)); X X wrap_needed = 0; X} X X X/************************************************************************ X * scr_forward_scroll(nlines) X *************************************************************************/ XPRIVATE scr_forward_scroll(nlines) Xint nlines; X{ X int i; X int start, end; X X#ifdef CHEAP_CGA X /* Since this routine will change our origin, we should first X * flush all pending output X */ X scr_flush_output(3); X#endif CHEAP_CGA X X /* max out nlines at NROWS; scrolling beyond that is meaningless */ X if(nlines > NROWS) nlines = NROWS; X X /* Zero out that section of video memory that we're going to use. */ X start = (cur_start + ((NROWS * NCOLS) << 1)) & SEG_MASK; X end = (start + ((nlines * NCOLS) << 1)) & SEG_MASK; X scr_clear_section(start, end); X X /* Adjust cur_start, and send it out to the CGA */ X cur_start = (cur_start + ((nlines * NCOLS) << 1)) & SEG_MASK; X X port_out(ADDR_PORT, START_LO); X port_out(DATA_PORT, (int)(cur_start >> 1) & 0xff); X port_out(ADDR_PORT, START_HI); X port_out(DATA_PORT, (int)(((cur_start >> 1) & 0xff00) >> 8)); X X X /* Adjust cursor row accordingly. Don't go off the top of the X * screen, though... X */ X X crow -= nlines; X if(crow < 0) crow = 0; X scr_set_cursor(crow, ccol); X X} X X#ifdef CHEAP_CGA X/************************************************************************ X * scr_flush_output(option) X * option - Indicates how hard we are to try to put the next X * character out. If 0, put out one character only X * if we're already in the blanking interval. If 1, X * then if we're already in the blanking interval, X * put out as many characters as we can during this X * blanking interval. If 2, wait for the next blanking X * interval, then put out as many chars as we can. If X * 3, then drain the whole queue, no matter HOW many X * blanking intervals it takes. X *************************************************************************/ XPRIVATE scr_flush_output(option) Xint option; X{ X X int i; X int max_chars; X int status; X X /* Don't bother if we have no work to do */ X if(oque_size == 0) { return; } X X switch(option) { X X case 0: X /* Output something, but only if blanking is on */ X if(vout(VIDEO_SEG, oque[oque_first].address, X (int)oque[oque_first].INTVAL)) { X X oque_first = (oque_first + 1) % NUM_OQUE_ENTRIES; X oque_size--; X } X break; X X case 1: X /* Output something while blanking is on */ X while(vout(VIDEO_SEG, oque[oque_first].address, X (int)oque[oque_first].INTVAL)) { X oque_first = (oque_first + 1) % NUM_OQUE_ENTRIES; X oque_size--; X if(oque_size == 0) break; X } X break; X X case 2: X /* Wait for blanking interval, then keep outputting X * until blanking interval is over. X */ X X voutsync(VIDEO_SEG, oque[oque_first].address, X (int)oque[oque_first].INTVAL); X X X oque_first = (oque_first + 1) % NUM_OQUE_ENTRIES; X oque_size--; X if(oque_size == 0) break; X X while(vout(VIDEO_SEG, oque[oque_first].address, X (int)oque[oque_first].INTVAL)) { X X oque_first = (oque_first + 1) % NUM_OQUE_ENTRIES; X oque_size--; X if(oque_size == 0) break; X X } X break; X X case 3: X /* Just drain the queue! */ X while(oque_size) { X voutsync(VIDEO_SEG, oque[oque_first].address, X (int)oque[oque_first].INTVAL); X X oque_first = (oque_first + 1) % NUM_OQUE_ENTRIES; X oque_size--; X } X break; X } X} X#endif CHEAP_CGA X X/************************************************************************ X * scr_shortescape(ch) X * X * At present, the following short escape sequences are supported: X * X * <none> X *************************************************************************/ XPRIVATE scr_shortescape(ch) Xchar ch; X{ X return; X} X X/************************************************************************ X * scr_longescape(ch) X * X * At present, the following long escape sequences are supported: X * X * ESC [ Pn A Cursor Up X * ESC [ Pn B Cursor Down X * ESC [ Pn C Cursor Right X * ESC [ Pn D Cursor Left X * ESC [ Pl;Pc H Set cursor (Pl = line, Pc = column) X * ESC [ Pl;Pc f Same as ESC [ Pl;Pc H X * ESC [ Pn K Line erase (0 = cursor to end of line, X * 1 = Beginning of line to cursor, 2 = whole line) X * ESC [ Pn J Screen erase (0 = Cursor to end of screen, 1 = X * beginning of screen to cursor, 2 = whole screen) X * ESC Pn;Pn... m Set character attributes. X *************************************************************************/ XPRIVATE scr_longescape(ch) Xchar ch; X{ X int i; X int first_addr; X int last_addr; X X switch (ch) { X case 'A': X if(esc_params[0] == 0) esc_params[0] = 1; X scr_set_cursor(crow - esc_params[0], ccol); X break; X X case 'B': X if(esc_params[0] == 0) esc_params[0] = 1; X scr_set_cursor(crow + esc_params[0], ccol); X break; X X case 'C': X if(esc_params[0] == 0) esc_params[0] = 1; X scr_set_cursor(crow, ccol + esc_params[0]); X break; X X case 'D': X if(esc_params[0] == 0) esc_params[0] = 1; X scr_set_cursor(crow, ccol - esc_params[0]); X break; X X case 'H': X case 'f': X /* Do 1-origin, but treat 0 as 1 */ X if(esc_params[0]) esc_params[0]--; X if(esc_params[1]) esc_params[1]--; X scr_set_cursor(esc_params[0], esc_params[1]); X break; X X case 'K': X switch(esc_params[0]) { X case 0: X /* Cursor to end of line */ X first_addr=(cur_start + (2 * crow * NCOLS) + (2 * ccol)) X & SEG_MASK; X last_addr = (cur_start + (2 * (crow + 1) * NCOLS) - 1) X & SEG_MASK; X break; X case 1: X /* Beginning of line to cursor */ X first_addr = (cur_start + (2 * crow * NCOLS)) & SEG_MASK; X last_addr = (cur_start + (2 * crow * NCOLS) + (2 * ccol)) X & SEG_MASK; X break; X X case 2: X /* Whole line */ X first_addr = (cur_start + (2 * crow * NCOLS)) & SEG_MASK; X last_addr = (cur_start + (2 * (crow + 1) * NCOLS) - 1) X & SEG_MASK; X break; X } X scr_clear_section(first_addr, last_addr); X break; X X case 'J': X switch(esc_params[0]) { X case 0: X /* Cursor to end of screen */ X first_addr=(cur_start + (2 * crow * NCOLS) + (2 * ccol)) X & SEG_MASK; X last_addr = (cur_start + (2 * NROWS * NCOLS) - 1) X & SEG_MASK; X X break; X X case 1: X /* Beginning of screen to cursor */ X first_addr = cur_start; X last_addr=(cur_start + (2 * crow * NCOLS) + (2 * ccol)) X & SEG_MASK; X break; X X case 2: X /* Whole screen */ X first_addr = cur_start; X last_addr = (cur_start + (2 * NROWS * NCOLS) - 1) X & SEG_MASK; X break; X } X scr_clear_section(first_addr, last_addr); X break; X X case 'm': X for(i = 0; i <= num_esc_params; i++) { X switch(esc_params[i]) { X char _temp; X X case 0: X /* Normal attributes */ X attrib = A_CHWHITE; X break; X case 1: X /* Bold */ X attrib |= A_INTENSE; X break; X case 4: X /* Underscore. Since the 6845 does not actually X * support underscoring (near as I can tell, X * anyway) then on the color monitor we'll X * implement it by using red characters. On X * a mono monitor, we'll use intense characters. X */ X if(color) { X if(attrib & A_CHWHITE) { X attrib &= ~(A_CHBLUE | A_CHGREEN); X } X else { X attrib &= ~(A_BGBLUE | A_BGGREEN); X } X } X else { X attrib |= A_INTENSE; X } X break; X X case 5: X /* Blink */ X attrib |= A_BLINK; X break; X X case 7: X /* Reverse video */ X _temp = attrib & A_CHWHITE; X attrib &= ~(A_CHWHITE); X attrib |= (_temp << 4); X break; X } X } X break; X } X X} X X X/************************************************************************ X * scr_clear_section(start, end) X *************************************************************************/ XPRIVATE scr_clear_section(start, end) Xint start; Xint end; X{ X X if(end < start) { X#ifdef CHEAP_CGA X vidsfill(VIDEO_SEG, start, (MAX_START - start) >> 1, 0x0700); X vidsfill(VIDEO_SEG, 0, end >> 1, 0x0700); X#else X vidfill(VIDEO_SEG, start, (MAX_START - start) >> 1, 0x0700); X vidfill(VIDEO_SEG, 0, end >> 1, 0x0700); X#endif X } X else { X#ifdef CHEAP_CGA X vidsfill(VIDEO_SEG, start, (end - start) >> 1, 0x0700); X#else X vidfill(VIDEO_SEG, start, (end - start) >> 1, 0x0700); X#endif CHEAP_CGA X } X} X X X/************************************************************************ X * Function: X * beep() X * X * Description: X * Ring the bell. X * X *************************************************************************/ Xbeep(f) Xint f; X{ X/* Sound a beep. The beep is kept short, because interrupts must be X * disabled during beeping, and it is undesirable to keep them off X * too long. This routine works by turning on the bits in port B of the X * 8255 chip that drive the speaker. X */ X X int k, x; X X lock(); X port_out(TIMER3, 0xB6); X port_out(TIMER2, f & BYTE); X port_out(TIMER2, (f>>8) & BYTE); X port_in(PORT_B, &x); X port_out(PORT_B, x|3); X for(k = 0; k < B_TIME; k++) ; X port_out(PORT_B, x); X unlock(); X} X X X/************************************************************************ X * Function: X * keyboard() X * X * Description: X * Keyboard interrupt service routine. X * X *************************************************************************/ XPUBLIC keyboard() X{ X/* A keyboard interrupt has occurred. Process it. */ X X int val, code, k; X char ch; X char parse_code(); X X /* Fetch the character from the keyboard hardware and acknowledge it. */ X port_in(KEYBD, &code); /* get the scan code for the key struck */ X port_in(PORT_B, &val); /* strobe the keyboard to ack the char */ X port_out(PORT_B, val | KBIT); /* strobe the bit high */ X port_out(PORT_B, val); /* now strobe it low */ X X X /* The IBM keyboard interrupts twice per key, once when depressed, once when X * released. Filter out the latter, ignoring all but the shift-type keys. X * The shift-type keys, 29, 42, 54, 56, 58, and 69 must be processed normally. X */ X k = code - 0200; /* codes > 0200 mean key release */ X if (k > 0) { X /* A key has been released. */ X if (k != 29 && k != 42 && k != 54 && k != 56 && k != 58 && k != 69) { X port_out(INT_CTL, ENABLE); /* re-enable interrupts */ X return; /* don't call tty_task() */ X } X } X X /* Check for CTRL-ALT-DEL, and if found, reboot the computer. */ X if (control && alt && code == DEL_CODE) reboot(); /* CTRL-ALT-DEL */ X X /* Certain function key combinations are used for debugging. Since X * we may want to use the function keys for applications, we'll X * require that one hit CTRL-ALT-Fx to do a debug function... X */ X X if(control && alt && IS_FKEY(code)) { X func_key(SCAN_CODE_TO_FKEY(code)); X port_out(INT_CTL, ENABLE); X return; X } X X /* If flow control is not on, then send this character up to the X * driver X */ X if (!fc_on) { X int send_charint = 0; X X /* Special function keys get handled specially here. */ X X if(IS_FKEY(code)) { X int fkey_number; X X fkey_number = SCAN_CODE_TO_FKEY(code) - 1; X send_charint = 1; X if(control) { X /* CTRL-Fkey sends up ESC Q <P+fkey_number> */ X kbd_insert_char(CHAR_ESC); X kbd_insert_char('Q'); X kbd_insert_char((char)('P' + fkey_number)); X } X else if (alt) { X /* ALT-Fkey sends the programmed string for that X * fkey. X */ X if(fkey_defs[fkey_number].fk_nchars > 0) { X for(k = 0; k < fkey_defs[fkey_number].fk_nchars; k++) { X kbd_insert_char(fkey_defs[fkey_number].fk_text[k]); X } X } X else { X send_charint = 0; X } X } X else { X /* Plain Fkey sends up ESC O <P+fkey_number> */ X kbd_insert_char(CHAR_ESC); X kbd_insert_char('O'); X kbd_insert_char((char)('P' + fkey_number)); X } X } X else { X ch = parse_code(code); X X if(ch) { X X if(ch == AT_SIGN) { ch = 0; } X kbd_inser_char(ch); X send_charint = 1; X } X } X X /* Build and send the interrupt message. */ X if(send_charint && !cint_sent) { X cint_sent = 1; X keybd_mess.m_type = TTY_CHAR_INT; X keybd_mess.TTY_LINE = 0; X interrupt(TTY, &keybd_mess); /* send a message to tty task */ X } X port_out(INT_CTL, ENABLE); X } else { X /* Flow control is on. Discard whatever was typed. */ X port_out(INT_CTL, ENABLE); /* re-enable 8259A controller */ X } X} X Xkbd_insert_char(ch) Xchar ch; X{ X /* Insert the specified char in the keyboard holding X * buffer, but only if there's enough room for it. X */ X if(con_size < CIBUFSZ) { X con_ibuf[con_last] = ch; X con_last = (con_last + 1) & CIBUFSZMASK; X con_size++; X } X} X X X X/************************************************************************ X * Function: X * parse_code(ch) X * X * Description: X * Translate the scan code for the key just struck or released. X * X * Arguments: X * ch Scan code to translate. X * X * Return Value: X * The character's ascii equivalent if possible. If no translation X * is to be done, returns 0. Returns special code AT_SIGN if we are X * returning an ASCII 0... (isn't return-value overloading wonderful?) X * X *************************************************************************/ XPRIVATE char parse_code(ch) Xchar ch; /* scan code of key just struck or released */ X{ X/* This routine can handle keyboards that interrupt only on key depression, X * as well as keyboards that interrupt on key depression and key release. X * For efficiency, the interrupt routine filters out most key releases. X */ X X int c, make, code; X X X c = ch & 0177; /* high-order bit set on key release */ X make = (ch & 0200 ? 0 : 1); /* 1 when key depressed, 0 when key released */ X X /* Translate scan code to character code */ X code = (shift1 || shift2 ? sh[c] : unsh[c]); X if (control && c < TOP_ROW) code = sh[c]; /* CTRL-(top row) */ X if (c > 70 && numlock) /* numlock depressed */ X code = (shift1 || shift2 ? unsh[c] : sh[c]); X X code &= BYTE; X if (code < 0200 || code >= 0206) { X /* Ordinary key, i.e. not shift, control, alt, etc. */ X if (capslock) X if (code >= 'A' && code <= 'Z') X code += 'a' - 'A'; X else if (code >= 'a' && code <= 'z') X code -= 'a' - 'A'; X if (alt) code |= 0200; /* alt key ORs 0200 into code */ X if (control) code &= 037; X if (code == 0) code = AT_SIGN; /* @ is 0100, so CTRL-@ = 0 */ X if (make == 0) code = 0; /* key release */ X return(code); X } X X /* Table entries 0200 - 0206 denote special actions. */ X switch(code - 0200) { X case 0: shift1 = make; break; /* shift key on left */ X case 1: shift2 = make; break; /* shift key on right */ X case 2: control = make; break; /* control */ X case 3: alt = make; break; /* alt key */ X case 4: if (make && caps_off) capslock = 1 - capslock; X caps_off = 1 - make; X break; /* caps lock */ X case 5: if (make && num_off) numlock = 1 - numlock; X num_off = 1 - make; X break; /* num lock */ X } X return(0); X} X X X/************************************************************************ X * Function: X * func_key(k) X * X * Description: X * Processes the given F-key. At present, F1 causes a process X * dump, and F2 causes a memory dump. X * X * Arguments: X * k Function key number X * X *************************************************************************/ Xfunc_key(ch) Xchar ch; X{ X if(ch == 1) p_dmp(); X if(ch == 2) map_dmp(); X} X X/************************************************************************ X * Function: X * con_bufin(minor, bufptr, maxchars) X * X * Description: X * Gets buffered input from the console layer. X * X * Arguments: X * minor Minor device. Must be zero. Ignored. X * bufptr Address of buffer to put characters into. X * maxchars Maximum number of characters to get. X * X * Return value: X * Returns number of characters actually gotten. X * X *************************************************************************/ XPUBLIC int con_bufin(minor, bufptr, maxchars) Xint minor; Xchar * bufptr; Xint maxchars; X{ X int nchars; X int i; X X X /* nchars = min(maxchars, con_size) */ X nchars = (maxchars < con_size) ? maxchars : con_size; X X for(i = 0; i < nchars; i++) { X bufptr[i] = con_ibuf[con_first]; X con_first = (con_first + 1) & CIBUFSZMASK; X con_size--; X } X cint_sent = 0; X return(nchars); X} X X Xconwrap() { wrap_enabled = 1; } Xconnowrap() { wrap_enabled = 0; } X / echo x - klib88.newstf gres '^X' '' > klib88.newstf << '/' X.globl _vidfill, _vidsfill, _vout, _voutsync X X|----------------------------------------------------------------------- X| Function: X| vidfill(seg, start, length, wordval) X| X| Description: X| Fills the specified portion of the video buffer with the X| specified word value. X| X| Arguments: X| seg Video segment number X| start Starting offset within video segment X| length Number of positions to fill X| wordval Word value <char, attrib> to stuff into buffer. X| X| Return Value: X| None. X| X|----------------------------------------------------------------------- X X_vidfill: X push bp X mov bp,sp | BP is frame pointer X push es X push bx | Save es & bx X push cx X mov ax,4(bp) | Move desired seg into ES X mov es,ax X mov ax,6(bp) | Get initial offset X mov bx,ax | into bx X mov ax,8(bp) | Get count X cmp ax,#0 | If zero, nothing to do X jz v120 X mov cx,ax | cx <- count X mov ax,10(bp) | Get value Xv100: X seg es | Use ES X mov (bx),ax | stuff byte X inc bx | Go to next word X inc bx X loop v100 | Keep going... Xv120: X pop cx X pop bx | restore regs X pop es X pop bp X ret X X|----------------------------------------------------------------------- X| Function: X| vidsfill(seg, start, length, wordval) X| X| Description: X| Fills the specified portion of the video buffer with the X| specified word value. Same as vidfill() except it only X| touches buffer RAM during the blanking interval. X| X| Arguments: X| seg Video segment number X| start Starting offset within video segment X| length Number of positions to fill X| wordval Word value <char, attrib> to stuff into buffer. X| X| Return Value: X| None. X| X|----------------------------------------------------------------------- X X_vidsfill: X push bp X mov bp,sp | BP is frame pointer X push es X push bx | Save regs X push cx X push dx X push si X mov ax,4(bp) | Move desired seg into ES X mov es,ax X mov ax,6(bp) | Get initial offset X mov si,ax | into si X mov ax,8(bp) | Get count X cmp ax,#0 | If zero, no work to do X jz v159 X mov cx,ax | cx <- count X mov ax,10(bp) | Get value X mov bx,ax | into bx X mov dx,#986 | Wait for blanking interval Xv150: X in X testb al,#1 X jz v160 X X seg es | Use ES X mov #0(si),bx | stuff byte X inc si | Go to next word X inc si X loop v150 | Keep going... Xv159: X pop si X pop dx X pop cx X pop bx | restore regs X pop es X pop bp X ret X Xv160: push cx | Delay loop, so we don't flicker display X mov cx,#32 | by sampling too often. Xv161: X loop v161 X pop cx X jmp v150 X X|----------------------------------------------------------------------- X| Function: X| vout(seg, addr, wordval) X| X| Description: X| Writes the specified word into the specified location in the X| video RAM if it is currently in the blanking interval. If X| not, then don't. Return a value indicating whether we could X| or not. X| X| Arguments: X| seg Video segment X| addr offset within the video segment X| wordval Value to write into the video RAM X| X| Return Value: X| 1 if write was successful. X| 0 if unsuccessful. X|----------------------------------------------------------------------- X X_vout: X push bp X mov bp,sp | bp is stack pointer X push es | Save regs: es, bx, cx, and dx X push bx X push cx X push dx X X mov ax,4(bp) | Get segment into es X mov es,ax X mov ax,6(bp) | Get address into bx X mov bx,ax X mov ax,8(bp) | Get wordval into cx X mov cx,ax X mov dx,#986 | dx = CGA status port number (0x3da) X in | Read port. X testb al,#1 | Test blanking bit. X jz v200 | Do nothing if zero X seg es X mov (bx),cx | In we go! X mov ax,#1 | Success! X jmp v210 Xv200: X mov ax,#0 | Failure... Xv210: X pop dx | Pop regs X pop cx X pop bx X pop es X pop bp X ret X X X|----------------------------------------------------------------------- X| Function: X| voutsync(seg, addr, wordval) X| X| Description: X| Wait for a blanking interval if necessary and write the X| specified word into the video RAM. X| X| Arguments: X| seg Video segment X| addr offset within the video segment X| wordval Value to write into the video RAM X| X| Return Value: X| None. X|----------------------------------------------------------------------- X X_voutsync: X push bp X mov bp,sp | bp is stack pointer X push es | Save regs: es, bx, cx, and dx X push bx X push cx X push dx X X mov ax,4(bp) | Get segment into es X mov es,ax X mov ax,6(bp) | Get address into bx X mov bx,ax X mov ax,8(bp) | Get wordval into cx X mov cx,ax X mov dx,#986 | dx = CGA status port number (0x3da) Xv300: X in | Read port. X testb al,#1 | Test blanking bit. X jz v350 | If zero, keep trying! X seg es X mov (bx),cx | In we go! X pop dx | Pop regs X pop cx X pop bx X pop es X pop bp X ret X Xv350: push cx | Delay loop X mov cx,#32 Xv351: X loop v351 X pop cx X jmp v300 X / echo x - mansi.tc gres '^X' '' > mansi.tc << '/' X# Termcap entry for MINIX ANSI tty driver (including function keys) X XMA|mansi|My own MINIX ansi driver:\ X :li#25:co#80:pc=\000:cd=\E[J:cm=\E[%i%d;%dH:bc=\010:\ X :k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:k5=\EOT:k6=\EOU:\ X :k7=\EOV:k8=\EOW:k9=\EOX:bs:cl=\E[H\E[J:ho=\E[H:\ X :so=\E[7m:se=\E[0m: /