/* * BRLTTY - A background process providing access to the console screen (when in * text mode) for a blind person using a refreshable braille display. * * Copyright (C) 1995-2021 by The BRLTTY Developers. * * BRLTTY comes with ABSOLUTELY NO WARRANTY. * * This is free software, placed under the terms of the * GNU Lesser General Public License, as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any * later version. Please see the file LICENSE-LGPL for details. * * Web Page: http://brltty.app/ * * This software is maintained by Dave Mielke . */ #include "prologue.h" #include #include #include #include #include #include #include #include #include #include #include "log.h" #include "system.h" #include "system_msdos.h" int _crt0_startup_flags = _CRT0_FLAG_LOCK_MEMORY; /* reduce image */ int _stklen = 0X2000; void __crt0_load_environment_file(char *_app_name) { return; } char **__crt0_glob_function(char *_arg) { return 0; } static void tsrExit (void) NORETURN; /* disable this bit of magic as it causes a page fault on exit */ #if 0 /* 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) { tsrExit(); } void _exit(int status) { tsrExit(); } int raise(int sig) { return 0; } void *signal(int signum, void*handler) { return NULL; } /* End undocumented way to make exception handling disappear */ #endif #define TIMER_INTERRUPT 0X08 #define DOS_INTERRUPT 0X21 #define IDLE_INTERRUPT 0X28 /* For saving interrupt and main contexts */ typedef char FpuState[(7+20)*8]; typedef struct { unsigned short segment; unsigned short offset; } DiskTransferAddress; typedef struct { FpuState fpu; DiskTransferAddress dta; unsigned short psp; } State; static State mainState; static State interruptState; static jmp_buf mainContext; static jmp_buf interruptContext; static int isBackgrounded = 0; /* whether we really TSR */ static unsigned long inDosFlagPointer; static unsigned long criticalOffset; /* prevent reentrancy */ static volatile int inTimerInterrupt = 0; static volatile int inIdleInterrupt = 0; static inline int inInterrupt (void) { return inTimerInterrupt || inIdleInterrupt; } static volatile unsigned long elapsedTickCount; static volatile unsigned long elapsedTickIncrement; static __dpmi_regs idleRegisters; static _go32_dpmi_seginfo origTimerSeginfo, timerSeginfo; static _go32_dpmi_seginfo origIdleSeginfo, idleSeginfo; /* handle Program Segment Prefix switch for proper file descriptor table */ static unsigned short getProgramSegmentPrefix (void) { __dpmi_regs r; r.h.ah = 0X51; __dpmi_int(DOS_INTERRUPT, &r); return r.x.bx; } static void setProgramSegmentPrefix (unsigned short segment) { __dpmi_regs r; r.h.ah = 0X50; r.x.bx = segment; __dpmi_int(DOS_INTERRUPT, &r); } /* handle Disk Transfer Address switch since djgpp uses it for FindFirst/FindNext */ static void getDiskTransferAddress (DiskTransferAddress *dta) { __dpmi_regs r; r.h.ah = 0X2F; __dpmi_int(DOS_INTERRUPT, &r); dta->segment = r.x.es; dta->offset = r.x.bx; } static void setDiskTransferAddress (const DiskTransferAddress *dta) { __dpmi_regs r; r.h.ah = 0X1A; r.x.ds = dta->segment; r.x.dx = dta->offset; __dpmi_int(DOS_INTERRUPT, &r); } unsigned short msdosGetCodePage (void) { __dpmi_regs r; r.h.ah = 0X66; r.h.al = 0X01; __dpmi_int(DOS_INTERRUPT, &r); return r.x.bx; } /* 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 (State *state) { saveFpuState(&state->fpu); getDiskTransferAddress(&state->dta); state->psp = getProgramSegmentPrefix(); } static void restoreState (const State *state) { restoreFpuState(&state->fpu); setDiskTransferAddress(&state->dta); setProgramSegmentPrefix(state->psp); } static unsigned short getTicksTillNextTimerInterrupt (void) { unsigned char clo, chi; outportb(0X43, 0XD2); clo = inportb(0X40); chi = inportb(0X40); return (chi << 8) | clo; } /* Timer interrupt handler */ static void timerInterruptHandler (void) { elapsedTickCount += elapsedTickIncrement; elapsedTickIncrement = getTicksTillNextTimerInterrupt(); if (!inInterrupt()) { inTimerInterrupt = 1; if (!setjmp(interruptContext)) longjmp(mainContext, 1); inTimerInterrupt = 0; } } /* Idle interrupt handler */ static void idleInterruptHandler (_go32_dpmi_registers *r) { if (!inInterrupt()) { inIdleInterrupt = 1; if (!setjmp(interruptContext)) longjmp(mainContext, 1); inIdleInterrupt = 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 tsrExit (void) { if (isBackgrounded) { unsigned long pspAddress = _go32_info_block.linear_address_of_original_psp; if (restore(TIMER_INTERRUPT, &timerSeginfo, &origTimerSeginfo) + restore(IDLE_INTERRUPT, &idleSeginfo, &origIdleSeginfo)) { /* failed, hang */ setjmp(mainContext); longjmp(interruptContext, 1); } { __dpmi_regs r; /* free environment */ r.x.es = _farpeekw(_dos_ds, pspAddress+0X2C); r.x.ax = 0X4900; __dpmi_int(DOS_INTERRUPT, &r); /* free Program Segment Prefix */ r.x.es = pspAddress / 0X10; r.x.ax = 0X4900; __dpmi_int(DOS_INTERRUPT, &r); } /* and return */ longjmp(interruptContext, 1); /* TODO: free protected mode memory */ } } /* go to background: TSR */ void msdosBackground (void) { __djgpp_set_ctrl_c(0); saveState(&mainState); if (!setjmp(mainContext)) { __dpmi_regs regs; /* set a chained Protected Mode Timer IRQ handler */ timerSeginfo.pm_selector = _my_cs(); timerSeginfo.pm_offset = (unsigned long)&timerInterruptHandler; _go32_dpmi_get_protected_mode_interrupt_vector(TIMER_INTERRUPT, &origTimerSeginfo); _go32_dpmi_chain_protected_mode_interrupt_vector(TIMER_INTERRUPT, &timerSeginfo); /* set a real mode DOS Idle handler which calls back our Idle handler */ idleSeginfo.pm_selector = _my_cs(); idleSeginfo.pm_offset = (unsigned long)&idleInterruptHandler; memset(&idleRegisters, 0, sizeof(idleRegisters)); _go32_dpmi_get_real_mode_interrupt_vector(IDLE_INTERRUPT, &origIdleSeginfo); _go32_dpmi_allocate_real_mode_callback_iret(&idleSeginfo, &idleRegisters); _go32_dpmi_set_real_mode_interrupt_vector(IDLE_INTERRUPT, &idleSeginfo); /* Get InDos and Critical flags addresses */ regs.h.ah = 0X34; __dpmi_int(DOS_INTERRUPT, ®s); inDosFlagPointer = msdosMakeAddress(regs.x.es, regs.x.bx); regs.x.ax = 0X5D06; __dpmi_int(DOS_INTERRUPT, ®s); criticalOffset = msdosMakeAddress(regs.x.ds, regs.x.si); /* We are ready */ isBackgrounded = 1; regs.x.ax = 0X3100; msdosBreakAddress(0X100/*psp*/ + _go32_info_block.size_of_transfer_buffer, 0, ®s.x.dx, NULL); __dpmi_int(DOS_INTERRUPT, ®s); /* shouldn't be reached */ logMessage(LOG_ERR, "TSR installation failed"); isBackgrounded = 0; } saveState(&interruptState); restoreState(&mainState); } unsigned long msdosUSleep (unsigned long microseconds) { unsigned long ticks; if (!isBackgrounded) { usleep(microseconds); return microseconds; } saveState(&mainState); restoreState(&interruptState); /* clock ticks to wait */ ticks = (microseconds * MSDOS_PIT_FREQUENCY) / UINT64_C(1000000); /* we're starting in the middle of a timer period */ { int wasEnabled = disable(); elapsedTickIncrement = getTicksTillNextTimerInterrupt(); elapsedTickCount = 0; if (wasEnabled) enable(); } while (elapsedTickCount < ticks) { /* wait for next interrupt */ if (!setjmp(mainContext)) longjmp(interruptContext, 1); /* interrupt returned */ } /* wait for Dos to be free */ setjmp(mainContext); /* critical sections of DOS are never reentrant */ if (_farpeekb(_dos_ds, criticalOffset) /* DOS is busy but not idle */ || (!inIdleInterrupt && _farpeekb(_dos_ds, inDosFlagPointer))) longjmp(interruptContext, 1); saveState(&interruptState); restoreState(&mainState); return (elapsedTickCount * UINT64_C(1000000)) / MSDOS_PIT_FREQUENCY; } int vsnprintf (char *str, size_t size, const char *format, va_list ap) { size_t alloc = 1024; char *buf; int ret; if (alloc < size) alloc = size; buf = alloca(alloc); ret = vsprintf(buf, format, ap); if (size > (ret + 1)) size = ret + 1; memcpy(str, buf, size); return ret; } int snprintf (char *str, size_t size, const char *format, ...) { va_list argp; int ret; va_start(argp, format); ret = vsnprintf(str, size, format, argp); va_end(argp); return ret; } void initializeSystemObject (void) { }