delorie.com/archives/browse.cgi   search  
Mail Archives: djgpp/2006/05/10/20:33:09

X-Authentication-Warning: delorie.com: mail set sender to djgpp-bounces using -f
From: Samuel Thibault <samuel DOT thibault AT ens-lyon DOT org>
Newsgroups: comp.os.msdos.djgpp
Subject: djgpp resident
Date: Thu, 11 May 2006 02:03:16 +0200
Organization: Debian GNU/Linux site
Lines: 312
Message-ID: <20060511000316.GA8654@bouh.residence.ens-lyon.fr>
NNTP-Posting-Host: cleo.labri.fr
Mime-Version: 1.0
X-Trace: news.u-bordeaux1.fr 1147305784 1344 147.210.8.93 (11 May 2006 00:03:04 GMT)
User-Agent: Mutt/1.5.9i-nntp
To: djgpp AT delorie DOT com
DJ-Gateway: from newsgroup comp.os.msdos.djgpp
Reply-To: djgpp AT delorie DOT com

--+QahgC5+KEYLbs62
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

Hi,

I have ported the Unix brltty daemon to DOS.  The idea was to replace
the the double-fork() technique by a setjmp followed by a call to TSR,
and then in interrupt/idle handlers, to longjmp back to the program.
I've then replaced in the daemon all blocking functions (they all had
timeouts) with equivalents that use my own tsr_usleep().

Attached is the file containing all hairy stuff (yes, brltty needs
FPU!), in case that can be useful to anyone (or even somehow integrated
to djgpp environment).

Comments and patches welcome of course ;)

Samuel

--+QahgC5+KEYLbs62
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="sys_msdos.c"

/* external interface:
 *
 * // equivalent of double-fork
 * void background(void);
 *
 * // wait for at least usec seconds, return actual sleep
 * unsigned long tsr_usleep(unsigned long usec);
 */

#include <setjmp.h>
#include <dpmi.h>
#include <pc.h>
#include <dos.h>
#include <go32.h>
#include <crt0.h>
#include <sys/farptr.h>

int _crt0_startup_flags = _CRT0_FLAG_LOCK_MEMORY;

/* reduce image */
int _stklen = 8192;

void __crt0_load_environment_file(char *_app_name) { return; }
char **__crt0_glob_function(char *_arg) { return 0; }

static void tsr_exit(void);
/* Start undocumented way to make exception handling disappear (v2.03) */
short __djgpp_ds_alias;
void __djgpp_exception_processor(void) { return; }
void __djgpp_exception_setup(void) { return; }
void __djgpp_exception_toggle(void) { return; }
int __djgpp_set_ctrl_c(int enable) { return 0; }
void __maybe_fix_w2k_ntvdm_bug(void) { }
void abort(void) { tsr_exit(); }
void _exit(int status) { tsr_exit(); }
int raise(int sig) { return 0; }
void *signal(int signum, void*handler) { return NULL; }
/* End undocumented way to make exception handling disappear */

#define TIMER_INT 0x8
#define DOS_INT 0x21
#define IDLE_INT 0x28

#define PIT_FREQ 1193180ULL

/* For saving interrupt and main contexts */

typedef char FpuState[(7+20)*8];
struct Dta {
  unsigned short seg;
  unsigned short off;
};

static struct State {
  FpuState fpu;
  struct Dta dta;
  unsigned short psp;
} intState, mainState;
static jmp_buf mainCtx, intCtx;

static int backgrounded; /* whether we really TSR */
static unsigned long inDosOffset, criticalOffset;
static volatile int inInt, inIdle; /* prevent reentrancy */
static volatile unsigned long elapsedTicks, toBeElapsedTicks;
static __dpmi_regs idleRegs;

static _go32_dpmi_seginfo origTimerSeginfo, timerSeginfo;
static _go32_dpmi_seginfo origIdleSeginfo,  idleSeginfo;

/* Handle PSP switch for proper file descriptor table */
static unsigned short getPsp(void) {
  __dpmi_regs r;
  r.h.ah = 0x51;
  __dpmi_int(DOS_INT, &r);
  return r.x.bx;
}

static void setPsp(unsigned short psp) {
  __dpmi_regs r;
  r.h.ah = 0x50;
  r.x.bx = psp;
  __dpmi_int(DOS_INT, &r);
}

/* Handle Dta switch since djgpp uses it for FindFirst/FindNext */
static void getDta(struct Dta *dta) {
  __dpmi_regs r;
  r.h.ah = 0x2f;
  __dpmi_int(DOS_INT, &r);
  dta->seg = r.x.es;
  dta->off = r.x.bx;
}

static void setDta(const struct Dta *dta) {
  __dpmi_regs r;
  r.h.ah = 0x1a;
  r.x.ds = dta->seg;
  r.x.dx = dta->off;
  __dpmi_int(DOS_INT, &r);
}

/* Handle FPU state switch */
#define saveFpuState(p) asm volatile("fnsave (%0); fwait"::"r"(p):"memory")
#define restoreFpuState(p) asm volatile("frstor (%0)"::"r"(p))

static void saveState(struct State *state) {
  saveFpuState(&state->fpu);
  getDta(&state->dta);
  state->psp = getPsp();
}
static void restoreState(const struct State *state) {
  restoreFpuState(&state->fpu);
  setDta(&state->dta);
  setPsp(state->psp);
}

static unsigned short countToNextTimerInt(void) {
  unsigned char clo, chi;
  outportb(0x43, 0xd2);
  clo = inportb(0x40);
  chi = inportb(0x40);
  return ((chi<<8) | clo);
}

/* Timer interrupt handler */
static void
timerInt(void) {
  elapsedTicks += toBeElapsedTicks;
  toBeElapsedTicks = countToNextTimerInt();

  if (!inIdle && !inInt) {
    inInt = 1;
    if (!setjmp(intCtx))
      longjmp(mainCtx, 1);
    inInt = 0;
  }
}

/* Idle interrupt handler */
static void
idleInt(_go32_dpmi_registers *r) {
  if (!inIdle && !inInt) {
    inIdle = 1;
    if (!setjmp(intCtx))
      longjmp(mainCtx, 1);
    inIdle = 0;
  }
  r->x.cs = origIdleSeginfo.rm_segment;
  r->x.ip = origIdleSeginfo.rm_offset;
  _go32_dpmi_simulate_fcall_iret(r);
}

/* Try to restore interrupt handler */
static int restore(int vector, _go32_dpmi_seginfo *seginfo, _go32_dpmi_seginfo *orig_seginfo) {
  _go32_dpmi_seginfo cur_seginfo;

  _go32_dpmi_get_protected_mode_interrupt_vector(vector, &cur_seginfo);

  if (cur_seginfo.pm_selector != seginfo->pm_selector ||
      cur_seginfo.pm_offset != seginfo->pm_offset)
    return 1;

  _go32_dpmi_set_protected_mode_interrupt_vector(vector, orig_seginfo);
  return 0;
}

/* TSR exit: trying to free as many resources as possible */
static void
tsr_exit(void) {
  __dpmi_regs regs;
  unsigned long pspAddr = _go32_info_block.linear_address_of_original_psp;

  if (restore(TIMER_INT, &timerSeginfo, &origTimerSeginfo) +
      restore(IDLE_INT,  &idleSeginfo,  &origIdleSeginfo)) {
    /* failed, hang */
    setjmp(mainCtx);
    longjmp(intCtx, 1);
  }

  /* free environment */
  regs.x.es = _farpeekw(_dos_ds,pspAddr+0x2c);
  regs.x.ax = 0x4900;
  __dpmi_int(DOS_INT, &regs);

  /* free PSP */
  regs.x.es = pspAddr/0x10;
  regs.x.ax = 0x4900;
  __dpmi_int(DOS_INT, &regs);

  /* and return */
  longjmp(intCtx, 1);

  /* TODO: free protected mode memory too */
}

/* go to background: TSR */
void
background(void) {
  saveState(&mainState);
  if (!setjmp(mainCtx)) {
    __dpmi_regs regs;

    /* set a chained Protected Mode Timer IRQ handler */
    timerSeginfo.pm_selector = _my_cs();
    timerSeginfo.pm_offset = (unsigned long)&timerInt;
    _go32_dpmi_get_protected_mode_interrupt_vector(TIMER_INT, &origTimerSeginfo);
    _go32_dpmi_chain_protected_mode_interrupt_vector(TIMER_INT, &timerSeginfo);

    /* set a real mode DOS Idle handler which calls back our Idle handler */
    idleSeginfo.pm_selector = _my_cs();
    idleSeginfo.pm_offset = (unsigned long)&idleInt;
    memset(&idleRegs, 0, sizeof(idleRegs));
    _go32_dpmi_get_real_mode_interrupt_vector(IDLE_INT, &origIdleSeginfo);
    _go32_dpmi_allocate_real_mode_callback_iret(&idleSeginfo, &idleRegs);
    _go32_dpmi_set_real_mode_interrupt_vector(IDLE_INT, &idleSeginfo);

    /* Get InDos and Critical flags addresses */
    regs.h.ah = 0x34;
    __dpmi_int(DOS_INT, &regs);
    inDosOffset = regs.x.es*16 + regs.x.bx;

    regs.x.ax = 0x5D06;
    __dpmi_int(DOS_INT, &regs);
    criticalOffset = regs.x.ds*16 + regs.x.si;

    /* We are ready */
    atexit(tsr_exit);
    backgrounded = 1;

    regs.x.ax = 0x3100;
    regs.x.dx = (/* PSP */ 256 + _go32_info_block.size_of_transfer_buffer) / 16;
    __dpmi_int(DOS_INT, &regs);

    /* shouldn't be reached */
    LogPrint(LOG_ERR,"Installation failed\n");
    backgrounded = 0;
  }
  saveState(&intState);
  restoreState(&mainState);
}

unsigned long
tsr_usleep(unsigned long usec) {
  unsigned long count;
  int intsWereEnabled;

  if (!backgrounded) {
    usleep(usec);
    return usec;
  }

  saveState(&mainState);
  restoreState(&intState);

  /* clock ticks to wait */
  count = (usec * PIT_FREQ) / 1000000ULL;

  /* we're starting in the middle of a timer period */
  intsWereEnabled = disable();
  toBeElapsedTicks = countToNextTimerInt();
  elapsedTicks = 0;
  if (intsWereEnabled)
    enable();

  while (elapsedTicks < count) {
    /* wait for next interrupt */
    if (!setjmp(mainCtx))
      longjmp(intCtx, 1);
    /* interrupt returned */
  }

  /* wait for Dos to be free */
  setjmp(mainCtx);

  /* critical sections of DOS are never reentrant */
  if (_farpeekb(_dos_ds, criticalOffset)
  /* DOS is busy but not idle */
   || (!inIdle && _farpeekb(_dos_ds, inDosOffset)))
    longjmp(intCtx, 1);

  saveState(&intState);
  restoreState(&mainState);

  return (elapsedTicks * 1000000ULL) / PIT_FREQ;
}

--+QahgC5+KEYLbs62--

- Raw text -


  webmaster     delorie software   privacy  
  Copyright © 2019   by DJ Delorie     Updated Jul 2019