vspacer
 
vspacer
 

Ups Server.



UPS Server

I used to work in a building with bad power, and would come in in the morning to find that up to half of the 17 servers scattered round the building had died during the night leaving corrupted databases behind.

So we invested in American Power Conversion UPS's .. and I wrote this...

A TSR that re-programs the 8250 chip, stuffs the keyboard buffer, messes with the interrupt enable register, and generally does things calculated to give Microsoft control freaks the heebie jeebies

TSR's have gone out of fashion, but it's entirely possible to have them in a Win32 environment, so maybe this code will rise again...

// 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 the 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 <dos.hx>                     // ESSENTIAL DECLS ONLY !!!!!
# include <stdio.hx>                   // puts, fcloseall ONLY!!!!
# include <string.hx>                  // strncmp ONLY !!!
# include <tsr.hx>                     // ESSENTIAL DECLS ONLY !!!!!
# include <intvec.hpp>
# include <bios.h>
# include <disp.h>
# include <sound.h>
# include <stdlib.h>                   // atoi,exit
# include <ctype.h>                    // 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]
//            :                          [Ascii  Scan]
//            :...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<argc;++j)
          { if (argv[j][0]=='/' && argv[j][2]=='=')
             { switch(toupper(argv[j][1]))
                { case 'D':
                     delay_mins=atoi(argv[j]+3);
                     break;
                  case 'N':
                     novell_mins=atoi(argv[j]+3);
                     break;
                  case 'S':
                     shutdown_mins=atoi(argv[j]+3);
                     break;
                  case 'R':
                     recharge_mins=atoi(argv[j]+3);
                     break;
                  default:
                     printf("Parameter not recognised\n");
                     exit(1);
                     break;
                }
             }
          }
//________________________________________________________________________
//      Re-Program the 8250 Serial chip
        int_off();
//      Open the Divisor Latch Address Bit (DLAB)
//
        outp(comx_baseport+3,inp(comx_baseport+3) | 0x80);
//      Set the Baud rate
        outp(comx_baseport,baud);     // LSB bits
        outp(comx_baseport+1,baud>>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(&regs,&regs);          // 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(&regs,&regs);          // 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(&regs,&regs);          // 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(&regs,&regs);       // 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(&regs,&regs);          // 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,&regs,&regs);
     cur_pg = regs.x.bx;
     regs.x.ax=3*256;
     int86(0x10,&regs,&regs);
     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,&regs,&regs);
    }
restore_cursor()
    {
     regs.x.ax=256;
     regs.x.bx=cur_pg;
     regs.x.cx=cur_s;
     int86(0x10,&regs,&regs);
     regs.x.dx=cur_p;
     regs.x.ax=2*256;
     int86(0x10,&regs,&regs);
    }


Back to top | ZDS Home | This article updated July 24 2000.