Logo Search packages:      
Sourcecode: jove version File versions

ibmpcdos.c

/**************************************************************************
 * This program is Copyright (C) 1986-2002 by Jonathan Payne.  JOVE is    *
 * provided by Jonathan and Jovehacks without charge and without          *
 * warranty.  You may copy, modify, and/or distribute JOVE, provided that *
 * this notice is included in all the source files and documentation.     *
 **************************************************************************/

#include "jove.h"

#ifdef IBMPCDOS   /* the body is the rest of this file */

#include "fp.h"   /* scr_putchar */
#include "chars.h"
#include "screen.h"
#include "term.h"

/* here come the actual emulation routines */

#include <dos.h>
#include <conio.h>

typedef unsigned char   BYTE;
typedef unsigned int    WORD;

#define VBS_set_cursor_position     0x02
#define VBS_get_cursor_position_and_size  0x03
#define VBS_select_active_display_page    0x05
#define VBS_scroll_window_up  0x06
#define VBS_scroll_window_down      0x07
#define VBS_read_character_and_attribute  0x08
#define VBS_write_character_and_attribute 0x09
#define VBS_TTY_character_output    0x0e
#define VBS_get_current_video_state 0x0f

#define VBSF_get_font_information   0x1130

#define VideoBIOS(r)    int86(0x10, (r), (r));

private BYTE
      c_attr = 0x07,    /* current attribute white on black */
      c_row = 0,  /* current row */
      c_col = 0;  /* current column */

int
      Txattr = 0x07,    /* VAR: text-attribute (white on black) */
      Mlattr = 0x70,    /* VAR: mode-line-attribute (black on white) */
      Hlattr = 0x10;    /* VAR: highlight-attribute */

void
getTERM()
{
      /* Check if 101- or 102-key keyboard is installed.
       * This test is apparently unreliable, so we allow override.
       * Courtesy of Ralph Brown's interrupt list.
       */
      char _far *kbd_stat_byte3 = (char _far *)0x00400096UL;
      enhanced_keybrd = (0x10 & *kbd_stat_byte3) != 0;
      pcSetTerm();
}

private void
setcolor(attr)
BYTE attr;
{
      c_attr = attr;
}

private void
set_cur()
{
      union REGS vr;

      vr.h.ah = VBS_set_cursor_position;
      vr.h.bh = 0;      /* video page 0 */
      vr.h.dl = c_col;
      vr.h.dh = c_row;
      VideoBIOS(&vr);
}

private void
get_cur()
{
      union REGS vr;

      vr.h.ah = VBS_get_cursor_position_and_size;
      vr.h.bh = 0;      /* video page 0 */
      VideoBIOS(&vr);
      c_col = vr.h.dl;
      c_row = vr.h.dh;
}

private BYTE
chpl()
{
      union REGS vr;

      vr.h.ah = VBS_get_current_video_state;
      VideoBIOS(&vr);
      return vr.h.ah;
}

#define cur_mov(r, c)   { c_row = (r); c_col = (c); set_cur(); }

private void
scr_win(op, no, ur, lr)
int op, no, ur, lr;
{
      union REGS vr;

      vr.h.ah = op;     /* scroll window up or down */
      vr.h.al = no;     /* number of rows to scroll */

      vr.h.ch = ur;     /* upper row */
      vr.h.cl = 0;      /* left column */
      vr.h.dh = lr;     /* lower row */
      vr.h.dl = CO-1;   /* right column */

      vr.h.bh = c_attr;
      VideoBIOS(&vr);
}

void
i_lines(top, bottom, num)
int top, bottom, num;
{
      scr_win(VBS_scroll_window_down, num, top, bottom);
}

void
d_lines(top, bottom, num)
int top, bottom, num;
{
      scr_win(VBS_scroll_window_up, num, top, bottom);
}

void
clr_page()
{
      SO_off();
      /* Note: VBS_scroll_window_up with a count of 0 clears the screen! */
      scr_win(VBS_scroll_window_up, 0, 0, ILI);
      cur_mov(0, 0);
}

private void
ch_out(c, n)
BYTE c, n;
{
      union REGS vr;

      vr.h.ah = VBS_write_character_and_attribute;
      vr.h.al = c;
      vr.h.bl = c_attr;
      vr.h.bh = 0;      /* video page 0 */
      vr.x.cx = n;
      VideoBIOS(&vr);
}

void
clr_eoln()
{
      ch_out(' ', CO-c_col);
}

/* Video mode setting derived from code posted to comp.os.msdos.programmer
 * by Joe Huffman 1990 August 15 (found on SIMTEL in msdos/screen/vidmode.zip)
 */

private BYTE
lpp()
{
      union REGS vr;
      int   lines;

      vr.x.ax = VBSF_get_font_information;
      vr.h.bh = 0;      /* we don't care which pointer we get back */
      vr.h.dl = 0;      /* default, if BIOS doesn't know how to this */
      VideoBIOS(&vr);
      lines = vr.h.dl;  /* number of last line on screen */
      switch (lines) {
      default:
            return lines + 1;
      case 25:
      case 28:
      case 43:
      case 50:
            return lines;     /* IBM EGA BUG!*/
      case 0:
            return 25;  /* Who knows?  Just a guess. */
      }
}

/* discover current video attribute */

private void
get_c_attr()
{
      union REGS vr;

      vr.h.dl = ' ';    /* write out a SPace, using DOS */
      vr.h.ah = 0x02;
      int86(0x21, &vr, &vr);

      vr.h.ah = VBS_TTY_character_output; /* backspace over it, using BIOS */
      vr.h.al = BS;
      vr.h.bh = 0;      /* page number 0 */
      VideoBIOS(&vr);

      vr.h.ah = VBS_read_character_and_attribute;     /* find out attribute */
      VideoBIOS(&vr);
      c_attr = vr.h.ah;
}

/* codes for selecting scan lines for alpha mode (service 0x12, function 0x30) */

#define EGA200    0
#define EGA350    1
#define EGA400    2

/* codes for selecting ROM font (function code for service 0x11)
 * Notes from Ralf Brown's Interrupt List:
 *      The routines called with AL=1xh are designed to be called only
 *      immediately after a mode set and are similar to the routines called
 *      with AL=0xh, except that:
 *          Page 0 must be active.
 *          Bytes/character is recalculated.
 *          Max character rows is recalculated.
 *          CRT buffer length is recalculated.
 *          CRTC registers are reprogrammed as follows:
 *               R09 = bytes/char-1 ; max scan line (mode 7 only)
 *               R0A = bytes/char-2 ; cursor start
 *               R0B = 0            ; cursor end
 *               R12 = ((rows+1)*(bytes/char))-1 ; vertical display end
 *               R14 = bytes/char   ; underline loc
 *                   (*** BUG: should be 1 less ***)
 *    the current block specifiers may be determined with INT 10/AH=1Bh,
 *      looking at offsets 2Bh and 2Ch of the returned data (VGA only)
 */

#define     EGA8x8      0x12  /* not 0x02 or 0x23 */
#define     EGA8x14     0x11  /* not 0x01 or 0x22 */
#define     EGA8x16     0x14  /* not 0x04 or 0x24 */

private void
EGAsetup(scanlines, font)
BYTE  scanlines;
BYTE  font;
{
      union REGS vr;
      vr.h.ah = VBS_select_active_display_page;
      vr.h.al = 0;      /* page 0 */
      VideoBIOS(&vr);

      vr.h.ah = 0x12;   /* request EGA information */
      vr.h.bl = 0x10;   /* function number */
      vr.x.cx = 0x0000;
      VideoBIOS(&vr);

      if (vr.x.cx != 0) {
            /* the display seems to be EGA or better */
            vr.h.ah = 0x12;   /* select scan lines for alpha mode */
            vr.h.al = scanlines;
            vr.h.bl = 0x30;
            VideoBIOS(&vr);

            /* Note: bh is left over from VideoBIOS call *before* last! */
            if (vr.h.bh == 0)
                  vr.x.ax = 0x0003; /* monochrome */
            else
                  vr.x.ax = 0x0007; /* 80x25 color text */
            VideoBIOS(&vr);

            vr.h.ah = 0x11;   /* load ROM font */
            vr.h.al = font;
            vr.h.bl = 0;      /* into block 0 */
            VideoBIOS(&vr);
      }
      get_c_attr();
}

private bool
set_lines(lines)
int   lines;
{
      switch (lines) {
      case 25:
            EGAsetup(EGA400, EGA8x16);
            break;
      case 28:
            EGAsetup(EGA400, EGA8x14);
            break;
      case 43:
            EGAsetup(EGA350, EGA8x8);
            break;
      case 50:
            EGAsetup(EGA400, EGA8x8);
            break;
      default:
            return NO;
      }
      return YES;
}

private bool      pc_set = NO;
private int unsetLI;

void
pcSetTerm()
{
      char  *t = getenv("TERM");

      if (!pc_set) {
            int   lines = lpp();

            unsetLI = lines;
            if (t != NULL) {
                  if (stricmp(t, "ega25") == 0)
                        lines = 25;
                  else if (stricmp(t, "ega28") == 0)
                        lines = 28;
                  else if (stricmp(t, "ega43") == 0)
                        lines = 43;
                  else if (stricmp(t, "ega50") == 0)
                        lines = 50;
            }
            if (lines != unsetLI && set_lines(lines))
                  pc_set = YES;
      }
      CO = chpl();
      if (CO > MAXCOLS)
            CO = MAXCOLS;
      LI = lpp();
      ILI = LI - 1;
      get_cur();
}

void
ttsize()
{
}

void
pcUnsetTerm()
{
      if (pc_set) {
            pc_set = NO;
            (void) set_lines(unsetLI);
      }
}

private void
line_feed()
{
      if (++c_row > ILI) {
            c_row = ILI;
            scr_win(VBS_scroll_window_up, 1, 0, ILI);
      }
      set_cur();
}

#define BELL_P 0x61                 /* speaker */
#define BELL_D 0x2dc                /* 550 hz  */
#define TIME_P 0x40                 /* timer */
#define TINI   182                  /* 10110110b timer initialization */

void
dobell(n)   /* declared in term.h */
int   n;
{
      unsigned char     spkr_state = inp(BELL_P);

      /* ??? Should accesses to timer be run with interrupts disabled?
       * It looks as if the timer would be in a bad state if a sequence
       * of accesses is interrupted. -- DHR 1995 Sept.
       */
      outp(TIME_P+3, TINI);
      outp(TIME_P+2, BELL_D&0xff);
      outp(TIME_P+2, BELL_D>>8);
      outp(BELL_P, spkr_state|3);         /* turn speaker on  */
      while (n-- > 0) {
#ifdef NEVER
            /* Control duration by counting half cycles of the audio tone.
             * We detect a half-cycle boundary by noting when the high-order
             * byte of the counter changes between zero and non-zero.
             * This should be unaffected by CPU speed.
             * We can't use the BIOS interval timer facility because
             * I think it would use the timer channel we're already using.
             */
            int   half_cycs = 200;  /* number of half-cycles to play */
            bool  hi_is_0 = NO;     /* detect zero transitions; initial lie unimportant */

            for (;;) {
                  (void) inp(TIME_P+2);   /* low-order counter 2 byte */
                  if ((inp(TIME_P+2) == 0) != hi_is_0) {
                        hi_is_0 = !hi_is_0;
                        if (--half_cycs <= 0)
                              break;
                  }
            }
#else
            unsigned int i = 0x8888;      /* a CPU-speed dependent duration */

            do ; while (--i > 0);
#endif
      }
      outp(BELL_P, spkr_state);
}

/* scr_putchar: put char on screen.  Declared in fp.h */

#ifdef USE_PROTOTYPES
/* char is subject to default argument promotions */
void
scr_putchar(char c)
#else
void
scr_putchar(c)
char c;
#endif
{
      switch (c) {
      case LF:
            line_feed();
            break;
      case CR:
            c_col = 0;
            set_cur();
            break;
      case BS:
            if (c_col > 0)
                  c_col--;
            set_cur();
            break;
      case CTL('G'):    /* ??? is this ever used? */
            dobell(1);
            break;
      default:
            ch_out((c), 1);
            if (++c_col > CO-1) {
                  c_col = 0;
                  line_feed();
            }
            set_cur();
            break;
      }
}

/* No cursor optimization on an IBMPCDOS, this simplifies things a lot.
 * Think about it: it would be silly!
 */

void
Placur(line, col)
int line,
    col;
{
      cur_mov(line, col);
      CapCol = col;
      CapLine = line;
}

private bool
      doing_so = NO,
      doing_us = NO;

private void
doattr()
{
      setcolor((doing_so? Mlattr : Txattr) ^ (doing_us? Hlattr : 0));
}

void
SO_effect(f)
bool f;
{
      doing_so = f;
      doattr();
}

void
US_effect(f)
bool  f;
{
      doing_us = f;
      doattr();
}

#endif /* IBMPCDOS */

Generated by  Doxygen 1.6.0   Back to index