// Copyright : ZDS Corporation 1992 // Proprietary information. // For use by ZDS Corporation employees only. // System : Tsr // Program : Upserver // Function : Designed to run on a (Novell) server-workstation equipped with // a special cable to intercept battery low status indication from // an American Power UPS and deal with it BEFORE Novell becomes // aware of it. // Author : Dave Goodall, ZDS Corporation (416) 845-3653 // Written : 14 Dec 90 // History : DD MMM YY By Ver Description // 14 Dec 90 Dave Goodall 1.01 New Code (Son of Ups0cy) // 18 Dec 90 Dave Goodall 1.02 Revised as result of Testing // to use special Serial Cable // 21 Dec 90 Dave Goodall 1.03 Add D-N-S state control // logic agreed with R. Stubbs // 22 Dec 90 Dave Goodall 1.04 Add Kbd buffer stuff logic // 02 Jan 90 Dave Goodall 1.05 Add - O/P Esc to Keyboard // at end of Delay Time // Move - O/P of SHIFT-ENTER // to end of Novell Time // Test esc with Rbase. //__________________________________________________________________________ // // Invoke : upserial /D=nn /N=nn /S=nn /R=nn // Defaults : 15 5 2 12 // // /D=nn is Delay in MINUTES after Pin 2 on the UPS goes from // low (-8v) to Hi (+12v) to indicate Commercial Power // is off before the program increments the SYSTEM YEAR. // // Note - If Pin two goes Low again during this period // to indicate power has been restored, the count down of // D is halted, and then incremented back by 1 for every // R minutes that pass with commercial power back on. // // This ensures that if closely spaced power outages occur // the program allows for the fact that the battery has // drained down to some extent, and has not been fully re- // charged in the intervening power up period. // // Note therefore that at any point in time D may be less // than the default or override time supplied when the // background program was originally loaded, depending on // the extent to which the battery has been drawn down. // // /N=nn is Delay in Minutes after program increments SYSTEM YEAR // before program passes on the bad news to Novell that the // UPS has lost Commercial Power. // // This delay is intended to allow time for Rbase programs // to become aware of the imminent power fail situation // via the SYSTEM YEAR change, and close down gracefully. // // /S=nn is Delay in Minutes after program passes on the Bad // news to Novell, that the program shuts down the UPS // inverter to prevent the battery being drained to total // dead flat. // // /R=nn is the recharge time in minutes that the UPS has to be // back on Commercial power to put back in in the battery // One minute of battery powering the inverter time. //__________________________________________________________________________ // /* Ups ..... Mouse Port Y Cable Specification :.. Serial Port ............................ : UPS DB9 Female Port : : SD LF LF LB SC GND : : ho ia ia oa wo : : uw ni ni wt cm : : tn el el : : : : : : : RS232C : :./.: : : :...../.: : :..........................: : : : : : : ............................ : Male DB9 Cable End : : 1 2 3 4 5 9 : :..........................: ^ v v v v v : : : : : : ...............................................: : : : : : : : : : : : : ...........................................: : : : : : : : : : : : : ........................... : : : : : : : : : : : : : : : .... To 9 > : : : : ..: : : : : : : : : : : : : : v : : : : : : : : ............................ : : : : : Female DB9 Cable End : : : : : : 1 2 3 4 5 9 : : : : : :..........................: : : : : : : : : ............................ : : : : : Male DB9 Cable End : : : : : :..........................: : : : : | : : : : | : : : : | ^ v ^ : | Female PS2/60 Serial Port 25 Pin Cable End PS2/60 Mouse Port ................................... ........................... : 4 20 5 8 22 6 2 7 : : 1 2 3 4 5 9 : : RTS DTR CTS CD RI DSR TD SG : : : :.................................: :.........................: ^ ^ v ^ : : : :........................ : : : : : : :................................. : : : : : : : : : : : ^ ^ v v : : : : Modem Modem Status Register : : Control : : : Register v v ^ ........... ................................... : : RTS DTR : : CD RI DSR CTS CD RI DSR CTS : : : : : del del del del : : : Bit Bit : : Bit Bit Bit Bit Bit Bit Bit Bit : : : 1 0 : : 7 6 5 4 3 2 1 0 : : :.........: :.................................: : v v : : : : ........:...............: : : : v ^ Line Control Register : ................................... : : DL SET STK EVN PAR STP DAT DAT : : : AB BRK PAR PAR ENB BIT BIT BIT : : : Bit Bit Bit Bit Bit Bit Bit Bit : : : 7 6 5 4 3 2 1 0 : : :.................................: : v : :...............................: Note : Refer to American Power Conversion Owners Manual Models // 100VX/800RT for UPS Remote Signal Connnector Pinout diagram. */ //__________________________________________________________________________ // // Function : -Goes resident and runs as a background program. // -Programs the 8250 chip as follows: // // -Checks the Modem Status Register for a change in CTS, which // has been wired to the UPS Pin 2 which is set up for RS232C // operation ie: // - Low (-8v) if Commercial Power is on // - High (+12v) if Commercial Power is off. Note that the UPS // will 'ride-through' 18 seconds of Commercial power out or // transient low voltage before it raises the alarm, so if // we get this, we probably have a serious outage. // // - The cable is wired so as to pass all the lines from the UPS // through to the PC Mouseport aka Novell, except lines 1 and 2. // // When we get a Commercial power out signal we start counting // down minutes from the Delay value D. If we get commercial // power back before we reach D=0, then we start incrementing // D back to the original default or override value, but more // slowly, as it take longer to recharge the battery than to // use it! (See explanation of /D=nn for more detail). // // // - When the Delay time has expired, we : // - Increment the system YEAR by 1. // - Eat any characters in the keyboard buffer and then // stuff an 'Esc' into it ie we simulate the depression // of the 'Esc' key on the keyboard. // // - Rbase programs active at this time will be in one of two // states: // a. Sitting in a CHOOSE or PAUSE instruction waiting for // the operator to make a selection (this is the most // common state usually). // b. Running through a loop of instructions which don't // require a user response. // // If they are in state 1 waiting for operator I/O the 'Esc' // will kick the Rbase program out of the CHOOSE or PAUSE // instruction. // // After each such instruction, the program should call a // routine to see if the system YEAR has changed since the // program started (we'll assume you saved the start-up value). // If it has goto a standard routine to put up a panel warning // the operator that power failure is imminent, then jump to // the normal Rbase close files and shutdown logic. // then goto DOS. // // If you have state 2 loops which may go on for a long time // before they return to a type 1 menu situation, then put a // check of the system year into the loop so that it gets // called at least every 15 minutes which is a bit less than // the UPS on-battery-support up time. // // - To cover the Rbase programs with sufficient time to cycle // into the year check logic and shutdown, we start counting // down a further delay time of N minutes. // // This is because on once Novell gets to hear about the // problem it starts broadcasting messages to all and sundry, // and flushing buffers, something we don't want it doing while // we're busy closing down open Rbases. // // - When the N minutes is up, we then use the Line Control // Register to pull up Pin 2 on the serial interface which // is wired through to Pin 2 on the Mouseport. // // After we're good and ready therefore (ie D+N) minutes after // initial power loss, Novell gets to hear the bad news and will // start broadcasting it. However, by that time, all the action // is over, and our Rbase files tucked safely up in bed. // // There is a restriction on this, in that if a long Rbase task // is in execution, we cannot recall it, as there is no known way // to checkpoint an Rbase 2.1 task, and run the year check // routine. However the majority of crashes should now be // evitable. // // - Lastly, as soon as the program has passed the bad noos onto // Novell, it starts a final countdown of S minutes. // // When this is up, it uses the Modem Control Register to // pull up pin 4 on the serial interface and leaves it up. // Pin 4 is wired to Pin 1 on the UPS port, and the appearance // of a hi voltage on this pin for a minimum of 5 seconds causes // the UPS to shut the inverter down. // // Once we're into the 'S' state, the program is set up to shut // down the UPS when S-time is up no matter whether commercial // power is restored or not. // // After this we don't care as we're dead, but the Battery // will thank us (thank you, thank you), as otherwise the // inverter will continue to pull power from the battery till // the battery is drained dead flat. // // Which as anyone who has left his lights on knows is not good // for battery life. By the way, you are sure you turned your // lights off aren't you? // // Note - we should find out if, after the inverter is shut // down via Pin 1, if the battery starts recharging if power // is restored, or if the UPS just lays there till the reset // switch is pressed. // //__________________________________________________________________________ // // Pins : The UPS appears to put out +12v on Pin 8 of the UPS port // permanently. This may be useful if you need to have a plus // voltage. // // Serial pin 2 was chosen to tickle the mouseport Pin 2 because // a master reset according to the IBM 8250 documentation always // pulls it low ie safe. // // Serial pin 4, was chosen to tickle the UPS shutdown Pin 1 // because although a master reset pulls it up, this is only // an extermely transient up, much too short to trigger inverter // shut-down. // // Serial Pin 20 (DTR) is still available to control something // if needed ie if APC see the light and, on future models of // their UPS's handle LOW BATTERY properly by providing the // 10c worth of transistors required to provide RS232C control // of that condition! (presumably on Pin 6). // //__________________________________________________________________________ // // 8250 Register Map and Usage REG8250.WKS // Port Addresses and Functions // // // MSB ----------------------Register------------------------------ LSB // Bits // 7 6 5 4 3 2 1 0 // // _____________________________________________________________________ // // PORT : COM1 = 0x3f8 COM2 = 0x2f8 COM3 = 0x3e8 COM4 = 0x2e8 // // Receiver Buffer Register (Read onlIf Line Control Register dlab=0 // Data Data Data Data Data Data Data Data // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 // // // OR // Transmitter Holding Reg (WRite onlIf Line Control Register dlab=0 // Data Data Data Data Data Data Data Data // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 // // // OR // LSB of Baud Rate Divisor Value If Line Control Register dlab=1 // Bit 7 Bit 6 Bit 5 Bit 4 Bit 3 Bit 2 Bit 1 Bit 0 // _____________________________________________________________________ // // In Which Case // // // PORT + 1 : COM1 = 0x3f9 COM2 = 0x2f9 COM3 = 0x3e9 COM4 = 0x2e9 // MSB of Baud Rate Divisor Value If Line Control Register dlab=1 // Bit 15 Bit 14 Bit 13 Bit 12 Bit 11 Bit 10 Bit 9 Bit 8 // // // Alternatively PORT+1 is // Interrupt Enable Register If Line Control Register dlab=0 // // 0 0 0 0 Enable Enable Enable Enable // Modem Received Transmit Received // Status Line Holding Data // Int Status Register Available // Empty // _____________________________________________________________________ // // PORT + 2 : COM1 = 0x3fa COM2 = 0x2fa COM3 = 0x3ea COM4 = 0x2ea // Interrupt Identification Register // // 0 0 0 0 0 InterruptInterrupt 0 // ID ID If Int // Bit 1 Bit 0 Pending // _____________________________________________________________________ // // PORT + 3 : COM1 = 0x3fb COM2 = 0x2fb COM3 = 0x3eb COM4 = 0x2eb // Line Control Register // // D Divisor Set Stick Even Parity Number Word Word // L Latch Break Parity Parity Enable Of Length Length // A Access Select Stop Select Select // B Bit Bits Bit 1 Bit 0 // _____________________________________________________________________ // // PORT + 4 : COM1 = 0x3fc COM2 = 0x2fc COM3 = 0x3ec COM4 = 0x2ec // Modem Control Register // // 0 0 0 Loop Out 2 Out 1 RTS DTR // // // _____________________________________________________________________ // // PORT + 5 : COM1 = 0x3fd COM2 = 0x2fd COM3 = 0x3ed COM4 = 0x2ed // Line Status Register // // 0 Transmit Transmit Break Framing Parity Overrun Data // Shift Holding Int- Error Error Error Ready // Register Register errupt // Empty Empty // _____________________________________________________________________ // // PORT + 6 : COM1 = 0x3fe COM2 = 0x2fe COM3 = 0x3ee COM4 = 0x2ee // Modem Status Register // Recv'd Ring Data Clear Delta Trailing Delta Delta // Line Indicator Set To Receive Edge Data Clear // Signal Ready Send Line Ring Set To // Detect Signal Indicator Ready Send // Det // _____________________________________________________________________ // // Function A: To set the baud rate for the chip: // // "Open the Dlab" // - eg set the most significant bit of the Line Control Register to 1 // by reading line control register byte, OR'ing it with 0x80 (10000000), // and writing the byte back to the register. // // - Turbo C: #define SERIO_BASEPORT 0x03f8 /* COM1 Base Port */ // outportb(SERIO_BASEPORT+3,inportb(SERIO_BASEPORT+3 | 0x80); // Zortech: outp(SERIO_BASEPORT+3,inp(SERIO_BASEPORT+3) | 0x80); // // Put the Baud rate into PORT (LSB) and PORT+1 (MSB) // // - Turbo C: int baud = 0x000c /* 9600 baud */ // outport(SERIO_BASEPORT,baud); // outport(SERIO_BASEPORT+1,baud>>8); /* right shift high // order bits */ // "Close the Dlab" // - eg reset msb of lct to 0 so that Port & Port +1 registers // revert to their usual (data register) functions. // // - Turbo C: outportb(SERIO_BASEPORT+3,inportb(SERIO_BASEPORT+3)&0x7f~); // Zortech: outp(SERIO_BASEPORT+3,inp(SERIO_BASEPORT+3) & 0x7f~); // // Function B: Set the parity, bits per char, stop bits // // - Turbo C: #define NOPARITY 0x0000 // #define EIGHTBITS 0x0003 // #define ONESTOPBIT 0x0000 // outportb(SERIO_BASEPORT+3,NOPARITY|EIGHTBITS|ONESTOPBIT); // // Function C: Set Interrupt Enable // // - Turbo C: outport(SERIO_BASEPORT+1,0x01); // // Function D: Set Modem Control // // - Turbo C: outport(SERIO_BASEPORT+4, 0x0f); // _____________________________________________________________________ // // 8259 Interrupt Controller Registers (8 bits each) // // Interrupt Mask Register (IMR) BIOS INTA01 // // Parallel Diskette Fixed COM1 COM2 RESERVED Keyboard Timer // Printer Disk PrimarySecondary // Serial Serial // SDLC SDLC // // Interrupt Request Register (IRR) BIOS INTA00 // // Edge,Sngl,buffered, 8086 mode // _____________________________________________________________________ // // // Masking: // // 0=Interrupt Enabled 1=Interrupt Disabled eg // ff 11111111 Disable all device interrupts // fe 11111110 Disable all except Timer Interrupt // bf 10111111 Enable diskette interrupt only // // Bitwise Logical Operations // // OR | XOR ^ // 0 + 0 = 0 0 + 0 = 0 // 0 + 1 = 1 0 + 1 = 1 // 1 + 0 = 1 1 + 0 = 1 // 1 + 1 = 1 1 + 1 = 0 // // AND & ~ Ones complement // 0 + 0 = 0 // 0 + 1 = 0 // 1 + 0 = 0 // 1 + 1 = 1 // //__________________________________________________________________________ // // Usage : The object of the exercise is to ensure that active Rbase // programs become aware of imminent power failure, and close // open Rbase files to prevent the corruption that inevitably // occurs if the system dies with them open. // // To make use of the background monitoring program any // program (Not just R:Base) programs that wants to should // save the system YEAR somewhere when it starts up. // // Such programs should then check the system date at any // convenient point (eg when cycling in a loop waiting for // keyboard input). If the year has changed, they can put // up the appropriate error message, close their files, and // shutdown. // //__________________________________________________________________________ // // Compiler : Zortech C++ V2.1 // // On the tsr_install the argument is TIMESLICE, as we want // popmain called every 18.2th of a second. // // Leave the TSR_DEBUG option in PERMANENTLY. // There's no space penalty involved, and given the very low // usage of the routine, no performance penalty either. If // you try & do no-no things in the handler, the TSR package // will popup appropriate naughty boy messages. // //__________________________________________________________________________ // // ZORTECH LIBRARY INCLUSIONS // (pared down to minimise program size) // #include // ESSENTIAL DECLS ONLY !!!!! #include // puts, fcloseall ONLY!!!! #include // strncmp ONLY !!! #include // ESSENTIAL DECLS ONLY !!!!! #include #include #include #include #include // atoi,exit #include // toupper //__________________________________________________________________________ // Function prototypes int _cdecl d_state(void); int _cdecl n_state(void); int _cdecl s_state(void); int _cdecl save_cursor(void); int _cdecl restore_cursor(void); int _cdecl inc_year(void); int _cdecl warn1(void); int _cdecl halt(void); int _cdecl diag(void); int _cdecl open_display(void); int _cdecl close_display(void); int _cdecl save_cursor(void); int _cdecl restore_cursor(void); //__________________________________________________________________________ // Serial Port Constants #define SEVENBITS 0x0002 #define EIGHTBITS 0x0003 #define ONESTOP 0x0000 #define TWOSTOP 0x0004 #define NOPARITY 0x0000 #define ODDPARITY 0x0008 #define EVENPARITY 0x0018 #define NOBREAK 0x0000 #define SETBREAK 0x0040 #define BAUD150 0x0300 #define BAUD300 0x0180 #define BAUD600 0x00c0 #define BAUD1200 0x0060 #define BAUD2400 0x0030 #define BAUD4800 0x0018 #define BAUD9600 0x000c #define BAUD19200 0x0006 #define COM1_INT 0x0c // COM1 Interrupt Number #define COM1_BASEPORT 0x03f8 // COM1 Base Port Address #define COM1_MASK 0xef // COM1 Interrupt Mask 8259 11101111 #define COM2_INT 0x0b // COM2 Interrupt Number #define COM2_BASEPORT 0x02f8 // COM2 Base Port Address #define COM2_MASK 0xf7 // COM2 Interrupt Mask 8259 11110111 // Serial Port Defaults int baud = BAUD150; int line_break = NOBREAK; int parity = NOPARITY; int stopbits = ONESTOP; int databits = EIGHTBITS; int comx_int = COM1_INT; // COM Interrupt Number int comx_baseport = COM1_BASEPORT; // COM Base Port Address int comx_mask = COM1_MASK; // COM Interrupt Mask 8259 //__________________________________________________________________________ // Command Line Parameter Defaults int delay_mins = 15; // Delay Time after Comercial power out int novell_mins = 5; // Further delay to tell Novell int shutdown_mins = 2; // Further delay till shutdown int recharge_mins = 12; // Mins to recharge 1 battery use minute // State-Counters char state = 'D'; // State - Can be 'D','N', or 'S' int d_sec; // Default/Override Delay time in secs int d_cnt; // Dynamic Delay time in secs int n_sec; // Default/Override Novell time in secs int n_cnt; // Dynamic Novell time in secs int s_sec; // Default/Override Shutdown time in secs int s_cnt; // Dynamic Shutdown time in secs int r_cnt; // Recharge counter //__________________________________________________________________________ // Bios Keyboard // // Addresses Function // // 0x417 Keyboard Shift Status Byte 1 // Bit> 7 6 5 4 3 2 1 0 // Use> INS CAPL NUML SCRL ALT CTRL LSHFT RSHFT // // 1=Active 0=Inactive // // 0x418 Keyboard Shift Status Byte 2 // Bit> 7 6 5 4 3 2 1 0 // Use> INS CAPL NUML SCRL HOLD PCJR NOT NOT // down down down down actv click used used // actv // // 0x419 Alt Keypad Entry // // 0x41a Pointer to head of keyboard buffer (Value 1E thru 3C) // 0x41b Pointer to head of keyboard byffer (Value always 0x00) // // 0x41c Pointer to tail of keyboard buffer (Value 1E thru 3C) // 0x41d Pointer to tail of keyboard byffer (Value always 0x00) // // Keyboard circular buffer : 16 x 2 byte entries // Each two byte entry representing a character is composed of // Byte 1 - The Ascii code value of the character // Byte 2 - The Scan code key value for the character // // Pointer // 0x41e Posn 1 Ascii Code byte + Scan Code key byte 0x1e // 0x420 2 0x20 // 0x422 3 0x22 // 0x424 4 0x24 // 0x426 5 0x26 // 0x428 6 0x28 // 0x42a 7 0x2a // 0x42c 8 0x2c // 0x42e 9 0x2e // 0x430 10 0x30 // 0x432 11 0x32 // 0x434 12 0x34 // 0x436 13 0x36 // 0x438 14 0x38 // 0x43a 15 0x3a // 0x43c 16 0x3c // // ...Pointer to Tail.... // : : // Eg 0x41a 0x41b 0x41c 0c41d 0x41e 0x41f ... etc more buff pairs // 1E 00 20 00 1B 01 // : <1st Buf Pos> // : // :...Pointer to Head..........: // // // Note : If the head pointer = the tail pointer then the circular // keyboard buffer is empty. int key; // scan byte + ascii byte char shift; // Keyboard Shift Status byte at 0x417 // 7 6 5 4 3 2 1 0 // INS CAPL NUML SCRL ALT CTRL LS RS // 6 byte constants to overlay addresses 0x41A thru 0x41f // char ESC_KEY[7] = {0x1E,0x00,0x20,0x00,0x1B,0x01}; //Esc in Buf bytes 1&2 char ENTER[7] = {0x1E,0x00,0x20,0x00,0x0D,0x1C}; //Enter in Buf bytes 1&2 //__________________________________________________________________________ // Working Variables unsigned scbuff[4000]; // Screen Buffer unsigned cur_pg,cur_p,cur_s; // Screen Buffer union REGS regs; // Input-Output Registers int cts; // Modem Status Register - CTS Bit 4 int rts; // Modem Control Register -0 RTS Bit 1 int s; // second change int X=20; // Warning panel exit count-down const int x; // panel exit count_down //________________________________________________________________________ unsigned TSR_HOTSHIFT = CTRL+LSHIFT; // Your hotkey LSHIFT/RSHIFT/CTRL/ALT char TSR_HOTSCAN = SCAN_Q; // combo SCAN_A thru SCAN_Z char tsr_fprint[20] = "Upserver V1.05"; // unique fingerprint string extern unsigned _tsr_timeslice; // REQUIRED FOR BACKGROUND PROGRAMS extern int _okbigbuf = 0; // minimise tsr memory requirement //________________________________________________________________________ main(argc,argv) int argc; char *argv[]; { // Parse Command Line switches (Or use defaults if none supplied) int j; for (j=1;j>8); // MSB bits // Close the DLAB outp(comx_baseport+3,inp(comx_baseport+3) & 0x7f); // Set Line Control Register Line-Break, Parity, Stopbits, Databits outp(comx_baseport+3,line_break | parity | databits | stopbits); // Set (Clear) Modem Control Register outp(comx_baseport+4,0x00); // Force RTS DTR low // printf("Modem Control Reg = %x\n",inp(comx_baseport+4)); // Set (Clear) Modem Status Register outp(comx_baseport+6,0x00); // // Set the Interrupt Enable Register to enable Modem Status Interrupts outp(comx_baseport+1,0x08); // 0001000 // printf("Int Enable Reg = %x\n",inp(comx_baseport+1)); // Set Interrupt ID Register outp(comx_baseport+2,0x00); // Set Bits 2-1 & 0 to 00 0 // printf("Int Ident Reg = %x\n",inp(comx_baseport+2)); //________________________________________________________________________ // Set 8259 Interrupt Controller to enable Serial Port Interrupts. // Get the Interrupt Mask Register from Port 0x21, Set Bit 4 low // to Enable the COM 1 serial port interrupt. // Note: For COM2 it's bit 3. outp(0x21,inp(0x21) & comx_mask); // 11101111 // printf("IMR Port 0x21 = %x\n",inp(0x021)); int_on(); // Initialise second equivalent comparison constant // and dynamic second counters d_sec=delay_mins*60; // Secs for comparison n_sec=novell_mins*60; // Secs for comparison s_sec=shutdown_mins*60; // Secs for comparison d_cnt=d_sec; // delay counter n_cnt=n_sec; // novell counter s_cnt=s_sec; // shutdown counter //________________________________________________________________________ // install tsr puts("Installing UPSERIAL power down background monitoring"); printf("Parameters D=%i N=%i S=%i R=%i\n",delay_mins,novell_mins,\ shutdown_mins,recharge_mins); int i; i=tsr_install(TIMESLICE|TSR_DEBUG); // if it returns, installation error has occured // if(i==1) puts("Can not load, program already loaded!\n"); else puts("Failed to install"); } //________________________________________________________________________ // POPMAIN is a special "reserved name", function, which the // TSR routines invoke when the hotkeys are pressed or the // timeslice occurs approx every 1/18th second (If the TIMESLICE // tsr_install option was set). void popmain(popmain) { // In here on timeslice every 18.2th of a second // If it's not an even second bounce right out again // so as to keep the overhead as low as possble regs.h.ah=0x2c; // Function 42 Get Time intdos(®s,®s); // Perform DOS 0x21 system call // returns ch=hh cl=mm dh=ss dl=100'ths if (s==regs.h.dh) return 1; // Bounce out else s=regs.h.dh; // On the second switch(state) { case 'D': d_state(); // D-Time processing break; case 'N': n_state(); // N-Time processing break; case 'S': s_state(); // Show-Time processing break; default: break; } return 1; // retain our handler } //________________________________________________________________________ // Delay state processing d_state() { cts=inp(comx_baseport+6) & 0x10; // Get MSR Bit 4 CTS if (cts==0x10) // If the battery is low { if (d_cnt==0) // and we've used all the delay time { inc_year(); // increment the year state='N'; // Set Novell state warn1(); // Display Warning panel // Flush the fifo keyboard buffer while (bioskey(1)) // While keys are available key=bioskey(0); // Read the key poke(0,0x41A,ESC_KEY,6); // Poke ESC into Keyboard buffer return 0; // we're outta here } else d_cnt=d_cnt-1; // decrement Delay (second) counter } else // If the battery is ok { if (d_cnt==d_sec) // and we havent used any battery time return 0; // we're outta here else { r_cnt=r_cnt+1; // increment recharge counter if (r_cnt==recharge_mins) // Yes - min is just a RATIO { // After r_min secs the battery has d_cnt=d_cnt+1; // recharged 1 discharge second's worth r_cnt=0; // clear recharge counter } } } } //________________________________________________________________________ // Novell state processing // // When the novell buffer covering Rbase shut-down is used up // Flush the keyboard buffer, stuff the keyboard status byte with // the Ctrl-Shift needed to clear the Novell error message : // '>> BATTERY LOW: CITYHALL will go down in 1 minute Press Ctrl Shift' // and wake Novell up. n_state() { cts=inp(comx_baseport+6) & 0x10; // Get MSR Bit 4 CTS if (cts==0x10) // If the battery is low { if (n_cnt==0) // and we've used all the novell time // Flush the Bios Keyboard Buffer { while (bioskey(1)) // While keys are available key=bioskey(0); // Read the key peek(0,0x417,&shift,1); // Get Kb Shift Status byte shift=shift |= 0x04; // Set CTRL bit ON poke(0,0x417,&shift,1); // Put it back poke(0,0x41A,ENTER,6); // Poke ENTER into Keyboard buffer // Pass on Battery Low via Pin 2 to // the Mouseport by forcing // LCR Bit 6 (Set Break) High outp(comx_baseport+3,inp(comx_baseport+3) | 0x40); state='S'; // Set Shutdown state return 0; // we're outta here } else n_cnt=n_cnt-1; // decrement Delay (second) counter } else // If the battery is ok { rts=inp(comx_baseport+3) & 0x40; // Get LCR Bit 6 (Set Break) if (rts==0x40) // If we told Novell it was low before // now commercial power is back again, // Pass on Battery Ok to // the Mouseport by forcing Pin 2 via // LCR Bit 6 (Set Break) Low outp(comx_baseport+3,inp(comx_baseport+3) & 0xbf); if (n_cnt==n_sec) // and we havent used any battery time { state='D'; // Recharged back to 'D' state return 0; // we're outta here } else { r_cnt=r_cnt+1; // increment recharge counter if (r_cnt==recharge_mins) // Yes - min is just a RATIO { // After r_min secs the battery has n_cnt=n_cnt+1; // recharged 1 discharge second's worth r_cnt=0; // clear recharge counter } } } } //________________________________________________________________________ // Shutdown state processing s_state() { halt(); // Imminent shutdown panel if (s_cnt==0) // If S-countdown complete // Signal UPS to shutdown the inverter // by forcing RTS (Pin 4) high by // setting Bit 1 of Modem Control Reg { outp(comx_baseport+4,inp(comx_baseport+4) | 0x02); goto loop; // wait for inverter shut-down } else { s_cnt=s_cnt-1; // countdown return 0; // exit function } loop: goto loop; // loop till inverter shuts down // and kills us. } //________________________________________________________________________ // The heart of the matter // increment the year field of the system date on UPS power down warning // inc_year() { regs.h.ah=0x2a; // Function 42 Get Date intdos(®s,®s); // Perform DOS 0x21 system call // returns dh=mm dl=dd cx=yy al=wkday regs.x.cx = regs.x.cx+1; // Increment year regs.h.ah=0x2b; // Function 42 Set Date intdos(®s,®s); // Perform DOS 0x21 system call // with dh=mm dl=dd cx=yy al=wkday } //________________________________________________________________________ // Display management // // Processor Halted Display // warn1() { sound_tone(5000,50,50); // Sound off! open_display(); // Open display disp_box(0,31,5,49,21,76); disp_fillbox(DISP_NORMAL+32,6,50,20,75); // Blank out box disp_move(7,51); disp_printf("ATTENTION!!"); disp_move(9,51); disp_printf("THE BATTERY IN"); disp_move(10,51); disp_printf("THE UPS IS"); disp_move(11,51); disp_printf("RUNNING LOW."); disp_move(13,51); disp_printf("PLEASE :"); disp_move(14,51); disp_printf(" - END PROGRAM"); disp_move(15,51); disp_printf(" - EXIT TO DOS"); x=X; // Set countdown delay while (x!=0) { regs.h.ah=0x2c; // Function 42 Get Time intdos(®s,®s); // Perform DOS 0x21 system call // returns ch=hh cl=mm dh=ss dl=100'ths if (s!=regs.h.dh) { disp_move(17,51); sound_tone(5000,50,50); // Sound off! disp_printf("%i ",x); s=regs.h.dh; x=x-1; // decrement countdown } } close_display(); // Close display } halt() { sound_tone(5000,50,50); // Sound off! open_display(); // Open display disp_box(0,31,5,49,21,76); disp_fillbox(DISP_NORMAL+32,6,50,20,75); // Blank out box disp_move(7,51); disp_printf("FINAL WARNING!"); disp_move(9,51); disp_printf("UPS SHUT DOWN"); disp_move(10,51); disp_printf("WILL TAKE PLACE"); disp_move(11,51); disp_printf("IN %i MINUTES",shutdown_mins); } // Diagnostic Display diag() { sound_tone(5000,50,50); // Sound off! open_display(); // Open display disp_box(0,31,5,49,21,76); disp_fillbox(DISP_NORMAL+32,6,50,20,75); // Blank out box disp_move(6,51); disp_printf("Battery is "); disp_move(6,62); if (cts==0x10) disp_printf("LOW"); else disp_printf("OK"); disp_move(8,51); disp_printf("State=%c Second=%i",state,s); disp_move(10,51); disp_printf("D M=%i S=%i C=%i",delay_mins,d_sec,d_cnt); disp_move(11,51); disp_printf("N M=%i S=%i C=%i",novell_mins,n_sec,n_cnt); disp_move(12,51); disp_printf("S M=%i S=%i C=%i",shutdown_mins,s_sec,s_cnt); disp_move(13,51); disp_printf("R R=%i (Ratio)",recharge_mins); disp_move(15,51); regs.h.ah=0x2a; // Function 42 Get Date intdos(®s,®s); // Perform DOS 0x21 system call // returns dh=mm dl=dd cx=yy al=wkday disp_printf("System Year = %i",regs.x.cx); disp_move(17,51); disp_printf("Line Control Reg3 = %x",inp(comx_baseport+3)); disp_move(18,51); disp_printf("Modem Control Reg4 = %x",inp(comx_baseport+4)); disp_move(19,51); disp_printf("Modem Status Reg6 = %x",inp(comx_baseport+6)); disp_move(20,51); disp_printf(" cts = %x",cts); bioskey(0); // Operator response close_display(); // Close display } open_display() { save_cursor(); disp_open(); peek(disp_base,0,&scbuff,4000); } close_display() { poke(disp_base,0,&scbuff,4000); disp_close(); restore_cursor(); } save_cursor() { regs.x.ax=15*256; int86(0x10,®s,®s); cur_pg = regs.x.bx; regs.x.ax=3*256; int86(0x10,®s,®s); cur_p = regs.x.dx; cur_s = regs.x.cx; regs.x.dx=(24*256)+80; regs.x.ax=2*256; regs.x.bx=cur_pg; int86(0x10,®s,®s); } restore_cursor() { regs.x.ax=256; regs.x.bx=cur_pg; regs.x.cx=cur_s; int86(0x10,®s,®s); regs.x.dx=cur_p; regs.x.ax=2*256; int86(0x10,®s,®s); }