home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 8 Other / 08-Other.zip / setlines.zip / setlines.c < prev    next >
C/C++ Source or Header  |  2002-01-11  |  34KB  |  634 lines

  1. /************************************************************************
  2.  *                                                                      *
  3.  * setlines:    use BIOS video interrupts to set colour text mode 3 on  *
  4.  *              VGA with 12, 14, 21, 25, 30, 34, 28, 43, 50, 60 lines:  *
  5.  *                                                                      *
  6.  * ROM font 8 x 8, 14, 16  - Some old VGA support only 50, 28, 25 lines *
  7.  * scanlines ------------  - Some old ATI report mode 5Bh for 30 x  80  *
  8.  *      200 | (25) 14  12  - Some old ATI report mode 23h for 25 x 132  *
  9.  *      350 |  43 (25) 21  - Some old ATI don't support VESA 108h, 10Ch *
  10.  *      400 |  50  28  25  - Some old VGA don't support VESA text modes *
  11.  *      480 |  60  34  30  - ANSI.SYS confused by NNx132, use VANSI.SYS *
  12.  *                         - Setlines -1 requires VESA power management *
  13.  *                         - Setlines +1 is a 2nd special case: 25 x 40 *
  14.  *                         - With 60 x 132 there is only ONE video page *
  15.  * Revision history:                                                    *
  16.  * 1.0      Support 12, 14, 21, 25, 28, 43, and 50 lines (EGA: 25, 43). *
  17.  * 1.1      Toggle screen height (25x80 and 28x80) as default,          *
  18.  *          replaced stdio.h fprintf() by smaller conio.h cputs().      *
  19.  * 1.2      Replaced int86x() by Watcom's smaller intr().               *
  20.  * 1.3      Support 30, 34, 60 lines based on 480 scan lines, uses the  *
  21.  *          standard VGA function INT 10 AX 1C02 "restore video state". *
  22.  * 2.0      Support 132 columns, specified as -21, -25, ..., -50, -60:  *
  23.  *          based on VESA modes 109, 10A, 10C.  Use VESA mode 108 for   *
  24.  *          60x80 if available.  But my ATI Mach32 is a bit stupid :-)  *
  25.  * 2.1      Toggle screen width (NNx80 and NNx132) as default.          *
  26.  * 2.2      Save current screen contents if width is the same, does not *
  27.  *          work as expected with PC DOS 7 ANSI.SYS, but VANSI.SYS or   *
  28.  *          no ANSI-driver are okay, and then even DOS accepts 60x132.  *
  29.  * 2.3      Patch BIOS data for get video mode result 3 instead of 83h, *
  30.  *          this prevents NC.EXE from clearing the saved screen because *
  31.  *          it did not understand the "keep video memory" bit 7 in 83h. *
  32.  * 2.4      Special argument: setlines -1 to switch VESA display power  *
  33.  *          OFF, awaits any key pressed before exit switching power ON. *
  34.  * 2.5      setlines 0 or setlines ? usage() return code == getlines(). *
  35.  *          setlines 1 supports 25x40 text mode 1 as 2nd special case.  *
  36.  * 2.6      Switch screen off during setlines() setting and scrolling.  *
  37.  *          Save current screen contents even if screen width changed.  *
  38.  * 3.0      Support ATI Rage Pro (missing VESA text modes replaced by   *
  39.  *          proprietary ATI modes 23 and 33 #ifdef ATI).                *
  40.  *                                                                      *
  41.  *                                        1998, 2002 by Frank Ellermann *
  42.  *                                                                      *
  43.  ************************************************************************/
  44.  
  45. #include        <stdlib.h>
  46. #include        <string.h>
  47. #include        <process.h>
  48. #include        <conio.h>
  49. #include        <dos.h>
  50.  
  51. #define BYTE    unsigned char
  52. #define WORD    unsigned short
  53.  
  54. #define VIDEO           0x10
  55. #define KEYB            0x16
  56. #define IDLE            0x28
  57. #define DOS             0x21
  58. #define DOS_FREEMEM     0x49    /* _dos_freemem() w/out WATCOM overhead */
  59. #define PSP_ENV_SEG     0x2C    /* environment segment in our PSP       */
  60.  
  61. #ifdef  __WATCOMC__
  62. #define SEG( nptr )     FP_SEG( nptr )
  63. #define OFS( nptr )     FP_OFF( nptr )
  64. #else
  65. #define SEG( nptr )     ((_segment)(void _far *)( nptr ))
  66. #define OFS( nptr )     ((WORD)(long)(void _far *)( nptr ))
  67. #define INTR_CF         0x0001  /* carry flag compatible with WATCOM C  */
  68. #define INTR_ZF         0x0040  /*  zero flag compatible with WATCOM C  */
  69.  
  70. union   REGPACK /* for WATCOM compatible intr( int, union REGPACK *r ): */
  71. {       struct  /* access on byte registers */
  72.         {       BYTE    al, ah, bl, bh, cl, ch, dl, dh;
  73.         }       h;
  74.         struct  /* access on word registers and flags */
  75.         {       WORD        ax,     bx,     cx,     dx;
  76.                 WORD    bp, si, di, ds, es;
  77.                 unsigned flags;
  78.         }       x;
  79. };
  80. #endif
  81.  
  82. #ifndef MK_FP
  83. #define MK_FP( s, o ) (((_segment)(s)):>((void _based(void) *)(o)))
  84. #endif
  85.  
  86. static  union REGPACK os_regs;          /* register work area for intr  */
  87.  
  88. /* -------------------------------------------------------------------- */
  89. /* MS C 6.0 int86x() is buggy: carry not cleared for interrupt < 0x25.  */
  90. /* MS C 6.0 has no intr(), which is much easier to use than int86x():   */
  91.  
  92. #ifndef __WATCOMC__                     /* WATCOM C 10.0 has intr()     */
  93. #pragma optimize( "leg", off )          /* optimize does not like _asm  */
  94.  
  95. void    _cdecl intr( int num, union REGPACK *R )
  96. {       _asm
  97.         {       push    bp              ;MS C 6 saves DI, SI
  98.                 push    ds              ;(please check this!)
  99.                 pushf                   ;save interrupt state
  100.  
  101.                 cli                     ;disable interrupts
  102.                 mov     al, byte ptr num
  103.                 mov     cs:byte ptr X+1, al
  104.  
  105. #if     defined( M_I86SM ) || defined( M_I86MM )
  106.                 mov     bx, R           ;DS:BX == *R
  107. #else
  108.                 lds     bx, R           ;DS:BX == *R
  109. #endif
  110.                 push    ds
  111.                 push    bx
  112.  
  113.                 mov     ax, [bx]R.x.ax
  114.                 mov     cx, [bx]R.x.cx
  115.                 mov     dx, [bx]R.x.dx
  116.                 mov     bp, [bx]R.x.bp  ;sets wanted BP
  117.                 mov     si, [bx]R.x.si
  118.                 mov     di, [bx]R.x.di
  119.                 mov     es, [bx]R.x.es  ;sets wanted ES
  120.  
  121.                 push    [bx]R.x.bx      ;save wanted BX
  122.                 mov     ds, [bx]R.x.ds  ;sets wanted DS
  123.                 pop     bx              ;sets wanted BX
  124.  
  125.              X: int     0               ;user interrupt
  126.  
  127.                 push    ds              ;save result DS
  128.                 push    bx              ;save result BX
  129.                 pushf                   ;save result flags
  130.  
  131.                 mov     bx, sp
  132.                 lds     bx, ss:[bx+6]   ;DS:BX == *R
  133.  
  134.                 pop     [bx]R.x.flags   ;sets result flags
  135.                 pop     [bx]R.x.bx      ;sets result BX
  136.                 pop     [bx]R.x.ds      ;sets result DS
  137.                 pop     bx
  138.                 pop     ds              ;DS:BX == *R
  139.  
  140.                 mov     [bx]R.x.es, es  ;sets result ES
  141.                 mov     [bx]R.x.di, di
  142.                 mov     [bx]R.x.si, si
  143.                 mov     [bx]R.x.bp, bp  ;sets result BP
  144.                 mov     [bx]R.x.dx, dx
  145.                 mov     [bx]R.x.cx, cx
  146.                 mov     [bx]R.x.ax, ax
  147.  
  148.                 popf                    ;reset interrupt state
  149.                 pop     ds              ;reset DS, BP for leave
  150.                 pop     bp              ;compiler resets SI, DI
  151. }       }
  152. #pragma optimize( "", on )              /* restore user's optimization  */
  153. #endif
  154. /*----------------------------------------------------------------------*/
  155.  
  156. static  void intr_video_on_os_regs( void )
  157. {       intr( VIDEO, &os_regs );        /* shorthand (saving 168 bytes) */
  158. }
  159. /*----------------------------------------------------------------------*/
  160. /* basically equivalent to _bios_keybrd(), shows use of os_regs.x.flags */
  161.  
  162. static  WORD intr_keyb( BYTE function )
  163. {       os_regs.h.ah = function;        intr( KEYB, &os_regs );
  164.  
  165.         if (( function & 0xEF ) == 1 && ( os_regs.x.flags & INTR_ZF ))
  166.                 return 0;               /* 0x01 or 0x11 invalid if NZ   */
  167.         else    return os_regs.x.ax;    /* else return valid result AX  */
  168. }
  169. /*----------------------------------------------------------------------*/
  170. /* if setlines -1 (display power off) worked, then this system supports */
  171. /* the enhanced keyboard functions 10h, 11h, and 12h (new XT or better) */
  172.  
  173. static  void idle( void )
  174. {       WORD shift;                     /* pressed key can be shift key */
  175.  
  176.         for (   shift =  intr_keyb( 0x12 );     /* initial shift status */
  177.                 shift == intr_keyb( 0x12 );     /* compare shift status */
  178.                 intr( IDLE, &os_regs ))         /* time slice for TSRs  */
  179.         {       if ( intr_keyb( 0x11 ))         /* if key pressed "eat" */
  180.                 {       intr_keyb( 0x10 );      return; /* key and exit */
  181. }       }       }
  182. /*----------------------------------------------------------------------*/
  183. /* vgapatch() handles 2 cases using function 1Ch save/restore VGA state */
  184. /* - VGA supports 480 scan lines, but function 12h BL 30h is limited to */
  185. /*   400 scan lines.  If VESA text modes 108h and 10Ch are unavailable, */
  186. /*   then vgapatch( 0 ) "restores" 480 scan lines (for mode 3 or 109h). */
  187. /* - VGA function 0Fh returns a video mode with "keep RAM" bit 7, which */
  188. /*   confuses some applications.  Therefore vgapatch( N ) clears bit 7  */
  189. /*   in BIOS data byte 40:87.  N is the number of rows, i.e. lines - 1. */
  190.  
  191. static  void vgapatch( BYTE rows )
  192. {       static BYTE * state;
  193.  
  194.         os_regs.x.ax = 0x1C00;          /* video 1C00h:  VGA state size */
  195.         os_regs.x.cx = (WORD)( rows ? 2 : 1 );
  196.         intr_video_on_os_regs();        /* CX 1: hardware, 2: BIOS etc. */
  197.         if ( os_regs.h.al != 0x1C       /* video 1Ch not supported (?)  */
  198.         ||   0 == ( state = malloc( 64 * os_regs.x.bx )))       return;
  199.  
  200.         os_regs.x.ax = 0x1C01;          /* video 1C01: save VGA state   */
  201.         os_regs.x.es = SEG( state );    /* ES:BX whatever state buffer  */
  202.         os_regs.x.bx = OFS( state );    intr_video_on_os_regs();
  203.  
  204.         switch ( rows )                 /* 0 hardware, else BIOS state: */
  205.         {       case 0: if ( state[ 0x20 + 0x40 ] != 0xD4       /* CRTC */
  206.                         ||   state[ 0x20 + 0x41 ] != 0x03 )     /* base */
  207.                                 break;  /* ignore unknown buffer layout */
  208.  
  209.                         state[ 0x20 + 0x09 ] |= 0xC0;
  210.                                         /* 3CC misc. output: enable 480 */
  211.                         state[ 0x20 + 0x10 ]  = 0x0D;
  212.                                         /* CRTC 06h vertical total 20Dh */
  213.                         state[ 0x20 + 0x11 ]  = 0x3E;
  214.                                         /* CRTC 07h overflow register   */
  215.                         state[ 0x20 + 0x1A ]  = 0xEA;
  216.                                         /* CRTC 10h start retrace  1EAh */
  217.                         state[ 0x20 + 0x1B ]  = 0x8C;
  218.                                         /* CRTC 11h vert. retrace end   */
  219.                         state[ 0x20 + 0x1C ]  = 0xDF;
  220.                                         /* CRTC 12h end   display  1DFh */
  221.                         state[ 0x20 + 0x1F ]  = 0xE7;
  222.                                         /* CRTC 15h start blanking 1E7h */
  223.                         state[ 0x20 + 0x20 ]  = 0x06;
  224.                                         /* CRTC 16h end   blanking      */
  225.                         break;
  226.                 default:                /* use rows to determine layout */
  227.                         if ( state[ 0x20 + 0x1E ] == rows      /* 40:84 */
  228.                         &&    ( state[ 0x3E + 3 ] &  0x80 ))   /* 40:87 */
  229.                         {       state[ 0x3E + 3 ] ^= 0x80;     /* bit 7 */
  230.                                 break;  /* Phoenix AT video BIOS layout */
  231.                         }               /* as documented in tech. ref.  */
  232.                         if ( state[ 0x20 + 0x1F ] == rows      /* 40:84 */
  233.                         &&    ( state[ 0x3F + 3 ] &  0x80 ))   /* 40:87 */
  234.                         {       state[ 0x3F + 3 ] ^= 0x80;     /* bit 7 */
  235.                                 break;  /* ATI layout (Phoenix +1 byte) */
  236.                         }               /* found in PCI onboard Mach32  */
  237.                         if ( state[ 0x20 + 0x3B ] == rows      /* 40:84 */
  238.                         &&    ( state[ 0x5B + 3 ] &  0x80 ))   /* 40:87 */
  239.                         {       state[ 0x5B + 3 ] ^= 0x80;     /* bit 7 */
  240.                                 break;  /* simple copy started at 40:49 */
  241.         }               }               /* as documented by Ralph Brown */
  242.  
  243.         os_regs.x.ax = 0x1C02;          /* video 1C02: restore state    */
  244.         intr_video_on_os_regs();        free( state );  /* just in case */
  245. }
  246. /*----------------------------------------------------------------------*/
  247. /* All VGA support 480 scan lines, but have no BIOS function to set it, */
  248. /* so either VESA modes 108h, 10Ch, or vgapatch( 0 ) enforce 480 lines. */
  249. /* VESA 108h is 60x80, 109h is 25x132, 10Ah is 43x132, 10Bh is 50x132,  */
  250. /* 10Ch is 60x132.  VESA 108h is the same as vgapatch( 0 ) mode 3, and  */
  251. /* VESA 10Ch is the same as vgapatch( 0 ) VESA 109h.  ATI Mach32 does   */
  252. /* not support 108h, 10Bh, or 10Ch directly, so these modes have to be  */
  253. /* derived from supported modes (this could be extended if necessary):  */
  254. /* 108h 60x 80: set mode 3, vgapatch( 0 ) 480 scan lines, load 8x8 font */
  255. /* 10Bh 50x132: set mode 109h 25x132 (uses 8x16 font) and load 8x8 font */
  256. /* 10Ch 60x132: set mode 109h, force 480 scan lines, then load 8x8 font */
  257.  
  258. static  void vesa( WORD mode )
  259. {       os_regs.x.ax = 0x4F02;          /* VESA 02: set VBE mode in BX  */
  260.         os_regs.x.bx = 0x8000 | mode;   /* bit 15: keep video buffer    */
  261.         intr_video_on_os_regs();        /* result AH == 00h successful  */
  262.         if ( os_regs.x.ax != 0x004F )   /* result AL == 4Fh supported   */
  263.         {       if ( mode ==  0x108 )   vgapatch( 0 );  /* "resets" 480 */
  264.                 if ( mode ==  0x10C )
  265.                 {       vesa( 0x109 );  vgapatch( 0 );  /* patch 25x132 */
  266. }       }       }
  267. /*----------------------------------------------------------------------*/
  268. /* Setting 200 (CGA), 350 (EGA), or 400 (VGA) scan lines works only if  */
  269. /* followed by a mode set.  Here we set mode 0x83 preserving the video  */
  270. /* memory (otherwise identical to mode 3).  Later vgapatch( N ) cheats  */
  271. /* silly programs not recognizing mode 0x83 by "restoring" text mode 3. */
  272.  
  273. static  char scanlines( BYTE mode )
  274. {       os_regs.h.ah = 0x12;            /* video 12h alternate select:  */
  275.         os_regs.h.bl = 0x30;            /* BL 30h text mode scan lines  */
  276.         os_regs.h.al = mode;            /* AL 0: 200, 1: 350, 2: 400    */
  277.         intr_video_on_os_regs();        /* result AL == 12h supported   */
  278.         mode = (BYTE)( os_regs.h.al == 0x12 );
  279.         os_regs.x.ax = 0x0083;          /* set mode 3 before font 111?h */
  280.         intr_video_on_os_regs();        /* bit 7: keep old video buffer */
  281.         return (char) mode;             /* 0 if no EGA/VGA, else 1 okay */
  282. }
  283. /*----------------------------------------------------------------------*/
  284. /* this function works only for EGA or better, but this is implicitly   */
  285. /* checked by the initial call of scanlines( 200 ) in setlines().       */
  286.  
  287. static  void loadfont( BYTE mode )      /* call only after scanlines()  */
  288. {       os_regs.x.bx = 0;               /* using character font block 0 */
  289.         os_regs.h.ah = 0x11;            /* AX 1111h 8x14 text mode font */
  290.         os_regs.h.al = mode;            /* AX 1112h 8x8  text mode font */
  291.         intr_video_on_os_regs();        /* AX 1114h 8x16 text mode font */
  292. }
  293. /*----------------------------------------------------------------------*/
  294. /* this function works only for EGA or better, but this is implicitly   */
  295. /* checked by the initial call of scanlines( 200 ) in setlines().       */
  296.  
  297. static  signed char getlines( void )
  298. {       os_regs.x.ax = 0x1130;          /* get font pointer information */
  299.         os_regs.h.bh = 0;               /* font 0 (dummy, any 0..7 ok.) */
  300.         intr_video_on_os_regs();        /* only EGA or VGA: DL == rows  */
  301.         os_regs.h.ah = 0x0F;            /* video 0Fh get mode, here use */
  302.         intr_video_on_os_regs();        /* result AH == columns 80, 132 */
  303.         if ( os_regs.h.ah == 40 )       /* I treat NNx40 as 25x40, i.e. */
  304.                 return 1;               /* mode 1 (a special case here) */
  305.         if ( os_regs.h.ah == 80 )       /* I treat anything else as 132 */
  306.                 return (signed char)(  1 + os_regs.h.dl );      /* pos. */
  307.         else    return (signed char)( -1 - os_regs.h.dl );      /* neg. */
  308. }
  309. /*----------------------------------------------------------------------*/
  310. /* off ==  0: switch VESA power and screen refresh ON,  ignore result 0 */
  311. /* off ==  1: (don't switch power), screen refresh OFF, ignore result 1 */
  312. /* off == -1: switch VESA power and screen refresh OFF, result 1 = done */
  313.  
  314. static  char display( signed char off )
  315. {       if ( off <= 0 )                 /* 0: display power on, -1: off */
  316.         {       os_regs.x.ax = 0x4F10;  /* VESA 10 BL 1 set power state */
  317.                 os_regs.h.bl = 1;       /* BH 4 (bit 2): off, BH 0: on  */
  318.                 os_regs.h.bh = (BYTE)( off ? 4 : 0 );
  319.                 intr_video_on_os_regs();
  320.                 off = (signed char)( off && os_regs.x.ax == 0x004F );
  321.         }
  322.         os_regs.h.ah = 0x12;            /* video 12h alternate select:  */
  323.         os_regs.h.bl = 0x36;            /* BL 36h screen AL 0/1 on/off  */
  324.         os_regs.h.al = (BYTE) off;      /* 0: screen refresh on, 1: off */
  325.         intr_video_on_os_regs();        return (char) off;
  326. }
  327. /*----------------------------------------------------------------------*/
  328. /* move a screen character with its attribute, used to adjust new width */
  329.  
  330. static  void move_char( BYTE srow, BYTE scol, BYTE trow, BYTE tcol )
  331. {
  332.         os_regs.h.bh = 0;                               /* page BH 0    */
  333.         os_regs.h.dh = srow;    os_regs.h.dl = scol;    /* source pos.  */
  334.         os_regs.h.ah = 2;       intr_video_on_os_regs();
  335.         os_regs.h.ah = 8;       intr_video_on_os_regs();
  336.         os_regs.x.cx = os_regs.x.ax;                    /* source char. */
  337.         os_regs.h.dh = trow;    os_regs.h.dl = tcol;    /* target pos.  */
  338.         os_regs.h.ah = 2;       intr_video_on_os_regs();
  339.         os_regs.h.al = os_regs.h.cl;                    /* character AL */
  340.         os_regs.h.bl = os_regs.h.ch;                    /* attribute BL */
  341.         os_regs.x.cx = 1;                               /* copy 1 char. */
  342.         os_regs.h.ah = 9;       intr_video_on_os_regs();
  343. }
  344. /*----------------------------------------------------------------------*/
  345. /* Clear MORE (i.e. new) columns and lines.  First adjust the screen to */
  346. /* MORE columns from lower right to upper left corner of the old image: */
  347.  
  348. static  void post_adjust(     BYTE oldrow, BYTE oldcol, BYTE newrow,
  349.                               BYTE newcol, BYTE colour, WORD cursor )
  350. {       if ( oldcol < newcol )          /* MORE columns, adjust screen: */
  351.         {       div_t   npos;           /* screen source row and column */
  352.  
  353.                 BYTE    nrow = (BYTE)( oldrow + 1 );    /* target row   */
  354.                 BYTE    ncol = (BYTE)( oldcol + 1 );    /* target col.  */
  355.                 int     opos = nrow * ncol;             /* source off.  */
  356.  
  357.                 for ( ; nrow-- ; ncol = (BYTE)( oldcol + 1 ))
  358.                 while ( ncol-- )
  359.                 {       npos = div( --opos, ( newcol + 1 ));
  360.                         move_char(      (BYTE)( npos.quot ),
  361.                                         (BYTE)( npos.rem  ),
  362.                                         nrow,   ncol );
  363.                 }
  364.                 os_regs.x.ax = 0x0600;  /* video 0600h clear window     */
  365.                 os_regs.h.bh = colour;  /* BH attribute for blank lines */
  366.                 os_regs.h.ch = 0;       /* upper left of added columns  */
  367.                 os_regs.h.cl = (BYTE)( oldcol + 1 );
  368.                 os_regs.h.dh = newrow;  /* lower line of added columns  */
  369.                 os_regs.h.dl = newcol;  /* lower right edge of window   */
  370.                 intr_video_on_os_regs();
  371.         }
  372.         if ( oldrow < newrow || cursor == 0xFFFF )
  373.         {       os_regs.x.ax = 0x0600;  /* video 0600h: clear window    */
  374.                 os_regs.h.bh = colour;  /* BH attribute for blank lines */
  375.                 os_regs.x.cx = 0;       /* upper left CH row 0 CL col 0 */
  376.                 os_regs.h.dh = newrow;  /* lower right row DH new lines */
  377.                 os_regs.h.dl = newcol;  /* lower right col DL new lines */
  378.  
  379.                 if ( cursor == 0xFFFF ) /* after failures clear screen, */
  380.                         cursor = 0;     /* else clear lower MORE lines  */
  381.                 else    os_regs.h.ch = (BYTE)( oldrow + 1 );
  382.  
  383.                 intr_video_on_os_regs();
  384.         }
  385.         os_regs.x.dx = cursor;          /* cursor restore resp. home    */
  386.         os_regs.h.ah = 2;               /* video 02h set pos., page BH  */
  387.         os_regs.h.bh = 0;               intr_video_on_os_regs();
  388. }
  389. /*----------------------------------------------------------------------*/
  390. /* Scroll LESS (i.e. lost) lines up.  Adjust the screen to LESS columns */
  391. /* if necessary from upper left to lower right corner of the new image: */
  392.  
  393. static  WORD init_adjust(     BYTE oldrow, BYTE oldcol,
  394.                               BYTE newrow, BYTE newcol, BYTE colour )
  395. {       WORD cursor;                    /* saved cursor position page 0 */
  396.  
  397.         os_regs.h.ah = 3;               /* video 03h get pos., page BH  */
  398.         os_regs.h.bh = 0;               intr_video_on_os_regs();
  399.         os_regs.x.ax = 0x0500;          cursor = os_regs.x.dx;
  400.         intr_video_on_os_regs();        /* video 05h set page, AL == 0  */
  401.  
  402.         if ( newrow < oldrow )          /* LESS lines, scroll screen up */
  403.         {       os_regs.h.ah = 0x06;    /* video 06h: scroll up window  */
  404.                 os_regs.h.al = (BYTE)( oldrow - newrow );
  405.                 os_regs.h.bh = colour;  /* BH attribute for blank lines */
  406.                 os_regs.x.cx = 0;       /* upper left row CH 0 col CL 0 */
  407.                 os_regs.h.dh = oldrow;  /* lower right row DH is oldrow */
  408.                 os_regs.h.dl = oldcol;  /* lower right col DL is oldcol */
  409.                 intr_video_on_os_regs();
  410.  
  411.                 os_regs.x.dx = cursor;   os_regs.h.dh += newrow;
  412.                 if ( oldrow <= os_regs.h.dh )   /* scroll cursor up too */
  413.                          os_regs.h.dh -= oldrow;
  414.                 else     os_regs.x.dx  = 0;     /* cursor out of bounds */
  415.                 cursor = os_regs.x.dx;  /* note pos. for restore at end */
  416.         }
  417.         if ( newcol < oldcol )          /* LESS columns, adjust screen: */
  418.         {       BYTE    nrow, ncol;     /* screen source row and column */
  419.                 div_t   npos;           /* screen target row and column */
  420.                 int     opos = 0;       /* target offset index from 0:0 */
  421.  
  422.                 for ( nrow = 0; nrow <= newrow; ++nrow )
  423.                 for ( ncol = 0; ncol <= newcol; ++ncol )
  424.                 {       npos = div( opos++, ( oldcol + 1 ));
  425.                         move_char(      nrow,   ncol,
  426.                                         (BYTE)( npos.quot ),
  427.                                         (BYTE)( npos.rem  ));
  428.                 }
  429.                 os_regs.x.dx = cursor;  /* adjust invalid cursor column */
  430.                 if ( newcol < os_regs.h.dl ) os_regs.h.dl = newcol;
  431.                 cursor = os_regs.x.dx;  /* as a very simple approach... */
  432.         }
  433.  
  434.         return cursor;
  435. }
  436. /*----------------------------------------------------------------------*/
  437. /* If scanlines( 200 ) does not work, then it's no EGA/VGA, but as the  */
  438. /* screen is already scrolled up in the case of LESS lines, it will be  */
  439. /* cleared completely.  The same is done for an invalid number of lines */
  440. /* like 50 on EGA, -25 without VESA, or other rubbish.                  */
  441.  
  442. static  char setlines( signed char wanted )
  443. {       BYTE border;                    /* ANSI.SYS saves only palette  */
  444.         BYTE colour;                    /* attribute for cleared areas  */
  445.         WORD cursor;                    /* saved cursor position page 0 */
  446.         BYTE oldrow, oldcol, newrow, newcol;    /* lower right position */
  447.  
  448.         /* ---- special case: -1 to switch power off ------------------ */
  449.  
  450.         if ( wanted == -1 ) return display( -1 );
  451.         display( 1 );                   /* screen refresh temporary OFF */
  452.  
  453.         /* ---- determine old and new row x col ----------------------- */
  454.         oldcol = newcol = 79;           /* anything else treated as 131 */
  455.  
  456.         if ((signed char)( newrow = (BYTE) wanted    ) < 0 )    /* abs: */
  457.         {       newrow = (BYTE)( 0 - (signed char) newrow ); newcol = 131;
  458.         }
  459.         if ((signed char)( oldrow = (BYTE) getlines()) < 0 )    /* abs: */
  460.         {       oldrow = (BYTE)( 0 - (signed char) oldrow ); oldcol = 131;
  461.         }
  462.         if ( --newrow == 0 ) ( newrow = 24, newcol = 39 );  /* 1: 25x40 */
  463.         if ( --oldrow == 0 ) ( oldrow = 24, oldcol = 39 );  /* 1: 25x40 */
  464.  
  465.         /* ---- save border colour and cursor ------------------------- */
  466.         os_regs.x.ax = 0x1008;          /* 1008h read overscan register */
  467.         intr_video_on_os_regs();        border = os_regs.h.bh;
  468.         os_regs.h.bh = 0;               /* mode set will force page 0   */
  469.         os_regs.h.ah = 8;               /* video 08h get char, page BH  */
  470.         intr_video_on_os_regs();        colour = os_regs.h.ah;
  471.  
  472.         cursor = init_adjust( oldrow, oldcol, newrow, newcol, colour );
  473.  
  474.         /* ---- select scan lines and font ---------------------------- */
  475.         if ( scanlines( 2 )) switch( wanted )   /* 400 VGA scan lines   */
  476.         {       case  21:       scanlines( 1 ); /* 350 EGA scan lines   */
  477.                 case  25:       loadfont( 20 ); /* use 8x16 generator   */
  478.                                 break;
  479.                 case  12:       scanlines( 0 ); /* 200 CGA scan lines   */
  480.                                 loadfont( 20 ); /* use 8x16 generator   */
  481.                                 break;
  482.                 case  14:       scanlines( 0 ); /* 200 CGA scan lines   */
  483.                 case  28:       loadfont( 17 ); /* use 8x14 generator   */
  484.                                 break;
  485.                 case  43:       scanlines( 1 ); /* 350 EGA scan lines   */
  486.                 case  50:       loadfont( 18 ); /* use 8x8  generator   */
  487.                                 break;
  488.                 case  30:       vesa(  0x108 ); /* 480 VGA scan lines   */
  489.                                 loadfont( 20 ); /* use 8x16 generator   */
  490.                                 break;
  491.                 case  34:       vesa(  0x108 ); /* 480 VGA scan lines   */
  492.                                 loadfont( 17 ); /* use 8x14 generator   */
  493.                                 break;
  494.                 case  60:       vesa(  0x108 ); /* 480 VGA scan lines   */
  495.                                 loadfont( 18 ); /* use 8x8  generator   */
  496.                                 break;
  497. #ifdef ATI
  498.                 case -21:       os_regs.x.ax = 0x00B3;
  499.                                 intr_video_on_os_regs();
  500.                                 loadfont( 20 ); /* use 8x16 generator   */
  501.                                 break;
  502.                 case -25:       os_regs.x.ax = 0x00A3;
  503.                                 intr_video_on_os_regs();
  504.                                 loadfont( 20 ); /* use 8x16 generator   */
  505.                                 break;
  506.                 case -28:       os_regs.x.ax = 0x00A3;
  507.                                 intr_video_on_os_regs();
  508.                                 loadfont( 17 ); /* use 8x14 generator   */
  509.                                 break;
  510.                 case -30:       os_regs.x.ax = 0x00A3;
  511.                                 intr_video_on_os_regs();    vgapatch( 0 );
  512.                                 loadfont( 20 ); /* use 8x16 generator   */
  513.                                 break;
  514.                 case -34:       os_regs.x.ax = 0x00A3;
  515.                                 intr_video_on_os_regs();    vgapatch( 0 );
  516.                                 loadfont( 17 ); /* use 8x14 generator   */
  517.                                 break;
  518.                 case -43:       os_regs.x.ax = 0x00B3;
  519.                                 intr_video_on_os_regs();
  520.                                 loadfont( 18 ); /* use 8x8  generator   */
  521.                                 break;
  522.                 case -50:       os_regs.x.ax = 0x00A3;
  523.                                 intr_video_on_os_regs();
  524.                                 loadfont( 18 ); /* use 8x8  generator   */
  525.                                 break;
  526.                 case -60:       os_regs.x.ax = 0x00A3;
  527.                                 intr_video_on_os_regs();    vgapatch( 0 );
  528.                                 loadfont( 18 ); /* use 8x8  generator   */
  529.                                 break;          /* only ONE video page  */
  530. #else
  531.                 case -21:       vesa(  0x10A ); /* VESA 10A, 43 x 132   */
  532.                                 loadfont( 20 ); /* use 8x16 generator   */
  533.                                 break;
  534.                 case -25:       vesa(  0x109 ); /* VESA 109, 25 x 132   */
  535.                                 loadfont( 20 ); /* use 8x16 generator   */
  536.                                 break;
  537.                 case -28:       vesa(  0x109 ); /* VESA 109, 25 x 132   */
  538.                                 loadfont( 17 ); /* use 8x14 generator   */
  539.                                 break;
  540.                 case -30:       vesa(  0x10C ); /* VESA 10C, 60 x 132   */
  541.                                 loadfont( 20 ); /* use 8x16 generator   */
  542.                                 break;
  543.                 case -34:       vesa(  0x10C ); /* VESA 10C, 60 x 132   */
  544.                                 loadfont( 17 ); /* use 8x14 generator   */
  545.                                 break;
  546.                 case -43:       vesa(  0x10A ); /* VESA 10A, 43 x 132   */
  547.                                 loadfont( 18 ); /* use 8x8  generator   */
  548.                                 break;
  549.                 case -50:       vesa(  0x109 ); /* VESA 109, 25 x 132   */
  550.                                 loadfont( 18 ); /* use 8x8  generator   */
  551.                                 break;          /* aka VESA 10B 50x132  */
  552.                 case -60:       vesa(  0x10C ); /* VESA 10C, 60 x 132   */
  553.                                 loadfont( 18 ); /* use 8x8  generator   */
  554.                                 break;          /* only ONE video page  */
  555. #endif
  556.                 case   1:                       /* mode 1 == 25 x  40   */
  557.                                 os_regs.x.ax = 0x0081;
  558.                                 intr_video_on_os_regs();
  559.                 default:        break;          /* unusual row number ? */
  560.         }
  561.         /* ---- restore border colour and cursor ---------------------- */
  562.  
  563.         os_regs.x.ax = 0x1001;          os_regs.h.bh = border;
  564.         intr_video_on_os_regs();        /* 1001h set  overscan register */
  565.  
  566.         if ( wanted == getlines() )     /* check result of operations:  */
  567.                 vgapatch( newrow );     /* okay, reset clear RAM flag   */
  568.         else    cursor = 0xFFFF;        /* else 0xFFFF indicates error  */
  569.  
  570.         post_adjust( oldrow, oldcol, newrow, newcol, colour, cursor );
  571.  
  572.         display( 0 );                   /* screen refresh again enabled */
  573.         return (char)( cursor != 0xFFFF );      /* 1: okay, 0: failure  */
  574. }
  575. /*----------------------------------------------------------------------*/
  576.  
  577. static  int usage( char const *name )
  578. {       cputs(  "\r\nusage: " );        cputs( name );
  579.         cputs(                          " [lines [command [args]]]\n"
  580.                 "\r\nlines = 12, 14, 21, 25, 28, 30, 34, 43, 50, 60,"
  581.                 "\r\nfor NNx132 use -21,-25,-28,-30,-34,-43,-50,-60.\n"
  582.                 "\r\nFor 25x40 use +1, +/-1 are special cases here."
  583.                 "\r\nOmit lines to toggle between NNx80 and NNx132.\n"
  584.                 "\r\nSpecify a command to execute it in a subshell"
  585.                 "\r\nwith the given number of lines.\n"
  586.                 "\r\nUse -1 to switch VESA display power OFF until"
  587.                 "\r\na key pressed or the subshell command returns."
  588.                 "\r\n"                                                  );
  589.  
  590.         return  getlines();             /* exit code current -60 ... 60 */
  591. }
  592. /************************************************************************/
  593.  
  594. int main( int argc, char *argv[] )
  595. {       int rc = 0;
  596.         signed char wanted, preset = getlines();
  597.         char *alias = argv[ 0 ];
  598.  
  599.         if ( argc == 1 )                /* no argument: toggle 80 / 132 */
  600.                 wanted = (signed char) -preset;
  601.         else    wanted = (signed char) atoi( argv[ 1 ] );
  602.  
  603.         if ( wanted == 0 ) return usage( alias );       /* invalid arg. */
  604.  
  605.         if ( ! setlines( wanted ))      /* bad number of rows or no VGA */
  606.         {       cputs( "\r\n" );        cputs( alias ); cputs( ": " );
  607.                 cputs( argc == 1 ? "toggle" : argv[ 1 ] );
  608.                 cputs( " not supported\r\n" );          return 1;
  609.         }
  610.  
  611.         if ( argc > 2 )                 /* try subshell command spawn   */
  612.         {
  613.                 os_regs.x.es = *(WORD _far *) MK_FP( _psp, PSP_ENV_SEG );
  614.                 os_regs.h.ah = DOS_FREEMEM;
  615.                 intr( DOS, &os_regs );  /* release environment memory   */
  616.  
  617.                 for ( rc = 2; rc <= argc; ++rc )        /* double shift */
  618.                         argv[ rc - 2 ] = argv[ rc ];    /* arguments    */
  619.  
  620.                 rc = spawnvp( P_WAIT, argv[ 0 ], argv );
  621.                 setlines( preset );     /* reset user's text mode lines */
  622.  
  623.                 if ( rc == -1 )         /* if spawn failed completely   */
  624.                 {       cputs( "\r\n" );        cputs( argv[ 0 ] );
  625.                         cputs( ": " );          cputs( strerror( errno ));
  626.                         cputs( "\r\n" );
  627.         }       }
  628.         else if ( wanted == -1 )        /* await any key pressed, then  */
  629.         {       idle(); display( 0 );   /* switch VESA display power ON */
  630.         }
  631.  
  632.         return rc;
  633. }
  634.