;
;       This file is part of the cFos project,
;       Copyright (c) 1993 by Martin Winkler, Christoph Lders
;
;       This file contains the C startup code and some assembler
;       subroutines.
;
;
;       You are allowed to use, copy, distribute, publish, redistribute
;       and/or modify this program under the terms of the 'cFos public
;       licence and warranty'.
;
;       This program is distributed in the hope that it will be useful,
;       but WITHOUT ANY WARRANTY; without even the implied warranty of
;       MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
;
;       See COPYING.CF for the 'cFos public license and warranty'.
;

                _NAME           equ     <'cFos'>
                _VSN            equ     <'0.94g'>
                _VSNNUM         equ     94

                MAXFUNC         equ     1bh
                MAXTIMERHOOKS   equ     5
                APP_CODE        equ     08fh

                STACK_LEN       equ     1024
                STACK_MAX       equ     5
                STACK_SIGN      equ     0471bh      ;some very rondom number

                RAX             equ     2
                RBX             equ     4
                RCX             equ     6
                RDX             equ     8
                RSI             equ     10
                RDI             equ     12
                RDS             equ     14
                RES             equ     16

                HydkProductCode equ     'DH'    ; Product Code for HyperDisk


                .286p

make_stack      macro
                cli
                call    build_stack
                les     si,es:[si]
                endm

unmake_stack    macro
                cli
                call    rebuild_stack
                endm


_TEXT           segment word public 'CODE'
_TEXT           ends

_DATA           segment word public 'DATA'
_DATA           ends

_BSS            segment word public 'BSS'
bssstart        equ     $
_BSS            ends

_STACK          segment para stack 'STACK'
bssend          equ     $
                org     $+STACK_LEN             ;for DOS' use
_STACK          ends

DGROUP          group   _DATA,_BSS,_STACK



extrn           _main:near
extrn           _c_timer:near
extrn           _c_states:near
extrn           _c_interface:near

ifndef  NDEBUG
extrn           _myaprintf:near
endif


;       The startup code is written to be compiled in SMALL model
;       and only serves SMALL model C code well.

_TEXT           segment word public 'CODE'
                assume  cs:_TEXT, ds:DGROUP

                db      'Silence is foo', 0

get_going:      mov     ax,DGROUP
                mov     ds,ax

                mov     si,offset DGROUP:banner
                call    c_disp
                mov     si,offset DGROUP:banner2
                call    c_disp


;       Don't need to check for DOS version, because DOS 1.xx didn't
;       supported .EXE's anyway

; ------------- Save some variables

                mov     ah,30h
                int     21h
                mov     DGROUP:__osver,ax

                mov     DGROUP:__psp,es         ;needs unchanged ES
                mov     ax,es:[2ch]
                mov     DGROUP:_aenvseg,ax

                mov     ah,34h                          ;get indos flag
                int     21h
                mov     word ptr DGROUP:__indos[0],bx
                mov     word ptr DGROUP:__indos[2],es



; ------------- Makeup a C-small-model-stack

;       Standard DOS conventions are to set up a stack at the top of
;       the allocated data space, with it OWN segment (SS != DS). C
;       won't like this habit, therefore we fiddle a bit with the
;       stackpointer, so SS == DS.  (I told you, this is only startup
;       code for SMALL model programs.)

                mov     ax,ss
                mov     bx,ds
                sub     ax,bx
                mov     cl,4
                shl     ax,cl
                add     ax,sp                   ;new SP

                mov     ss,bx
                mov     sp,ax



; ------------- Clear BSS segment

;       We assume that _BBS and _STACK are GROUPed, and _STACK lies
;       immediately behind _BBS;  so we can take the first label
;       in _BSS and the first in _STACK for our calculations

                mov     di,offset DGROUP:bssstart
                mov     cx,offset DGROUP:bssend
                sub     cx,di

                mov     ax,_DATA
                mov     es,ax
                xor     ax,ax

                push    cx
                shr     cx,1
                rep     stosw
                pop     cx
                and     cx,1
                rep     stosb


; ------------- Determine if run under i386

                mov     ax,0f000h               ;286/386/486
                push    ax
                popf
                pushf
                pop     ax
                and     ax,0f000h

                mov     al,0
                jz      cp286                   ;--> 80286

                mov     al,1
                mov     byte ptr cs:[_TEXT:no386?+1],0  ;turn into 'jmp $+2'
cp286:  ;        mov     DGROUP:_use_386,al


; ------------- Call _main ()

                mov     es,DGROUP:__psp
                mov     si,81h
                push    es
                push    si

                mov     bl,es:[si-1]
                mov     bh,0
                mov     byte ptr es:[bx+81h],0          ;null-terminate it


;       _main may now be called.  C has it's beloved stack and everything's
;       fine sofar.  Be aware of the different commandline refence:
;
;       int main (char _FAR *arg);

                call    _main


; ------------- .. and exit

                mov     ah,4ch
                int     21h



; ----------------------------------------------------------------------
;       INT 08 Handler
; ----------------------------------------------------------------------

;       Like X00, we call all functions that hooked into the timer chain
;       and our own c_timer() only if the timer value (dword) at 40:6c
;       has changed and if we aren't already in a timer event. Our own
;       timer routine is also not used if we are in a CAPI event.
;
;       The original functiosn are called first to allow them to do
;       'important' work.

public          _int08_entry
extrn           _in_event:word

_int08_entry:   cmp     cs:in_timer,0
                jz      not_in_timer
                jmp     dword ptr cs:_old_int08

not_in_timer:   inc     cs:in_timer
                pushf
                cli
                call    dword ptr cs:_old_int08


                make_stack
                sti

                mov     ax,DGROUP
                mov     ds,ax

                cmp     word ptr DGROUP:_in_event,0
                jnz     call_user_timer


;       We compute a _numticks variable, which holds the number of ticks
;       since the last call of the _c_timer function. This may be != 1
;       if e.g. the last tick occured in a CAPI event and c_timer() wasn't
;       called.

                mov     ax,40h
                mov     es,ax

                mov     ax,es:[06ch]
                mov     dx,es:[06ch+2]
                mov     bx,DGROUP:timer_lo
                mov     cx,DGROUP:timer_hi

                cmp     dx,cx
                jb      timer_midnight
                ja      timer_norm
                cmp     ax,bx
                jb      timer_midnight

timer_norm:     mov     DGROUP:timer_hi,dx
                mov     DGROUP:timer_lo,ax

timer_sub:      sub     ax,bx
                mov     DGROUP:_numticks,ax

                call    _c_timer                ;void c_timer (void)


call_user_timer:mov     bx,offset timer_chain
                mov     cx,MAXTIMERHOOKS

next_timer_call:mov     ax,[bx]
                or      ax,[bx+2]
                jz      no_timer_call

                sti
                call    dword ptr [bx]

no_timer_call:  add     bx,4
                loop    next_timer_call

no_timer:       unmake_stack
                dec     cs:in_timer
                iret


timer_midnight: mov     DGROUP:timer_hi,dx
                mov     DGROUP:timer_lo,ax
;                add     dx,18h                  ;add 1800b0h (ticks per day)
                add     ax,0b0h
                jmp     short timer_sub         ;set timer_lo, timer_hi on init, BEFORE statesinit is set to 1

in_timer        db      0



; ----------------------------------------------------------------------
;       CAPI Event Handler
; ----------------------------------------------------------------------

public          _capi_event_entry

_capi_event_entry proc  far

                make_stack
                call    _c_states
                unmake_stack

                iret

_capi_event_entry endp



; ----------------------------------------------------------------------
;       FOSSIL INT 14 handler
; ----------------------------------------------------------------------

public          _applid
public          _int14_entry
public          _old_int08
public          _old_int14
public          _ptr2segs

_ptr2segs       dd      0
_old_int08      dd      0
_old_int14      dd      0
                db      'cFos'                  ;XINU problem

_int14_entry:   sti
                jmp     short past_signature
                nop

_applid         dw      0                       ;holds the application ID
                dw      1954h                   ;to be checked by apps
                db      MAXFUNC                 ;max FOSSIL func #
                dw      0
past_signature:


;-------------- check range of function number

                cmp     ah,MAXFUNC
                jbe     standard_func

                cmp     ah,0bfh
                ja      func_too_large
                cmp     ah,APP_CODE
                je      own_funcs
                cmp     ah,80h
                jae     call_user
                cmp     ah,7eh
                jb      func_too_large
                jmp     install_user


;       This calls a FOSSIL user appendage;  The only one commonly known
;       is a video FOSSIL by bob hartman.  anyway, this func handles all
;       appendages ;-)

call_user:
                sub     sp,2            ;used for RET far

                push    bp
                mov     bp,sp           ;bp+4 -> offset
                                        ;bp+6 -> segment
                                        ;bp+8 -> flags
                push    ax
                push    bx

                mov     bh,0                    ;get entry point
                mov     bl,ah
                sub     bl,080h
                add     bl,bl
                add     bl,bl
                add     bx,offset _TEXT:user_call_table

                mov     ax,cs:[bx]                 ;offset
                or      ax,cs:[bx+2]               ;segment
                jz      user_not_in_use

                mov     ax,[bp+6]               ;makeup a nice return stack
                mov     [bp+8],ax
                mov     ax,[bp+4]
                mov     [bp+6],ax

                mov     ax,cs:[bx]
                mov     [bp+2],ax               ;make offset
                mov     ax,cs:[bx+2]
                mov     [bp+4],ax               ;make segment

                pop     bx
                pop     ax
                pop     bp

                db      0cbh                    ;ret far

user_not_in_use:pop     bx
                pop     ax
                pop     bp
                add     sp,2

func_too_large: stc
                mov     ax,0ff00h
                db      0cah, 2, 0             ;ret far 2



own_funcs:      push    ax
                call    __debug_funcs
                add     sp,2
                jmp     short byebye2                 ;iret


; ----------------------------------------------------------------------
;       main FOSSIL function handler
; ----------------------------------------------------------------------

standard_func:  push    si
                push    ds

                mov     si,DGROUP
                mov     ds,si

                xchg    al,ah
                mov     si,ax
                and     si,0ffh
                xchg    al,ah
                shl     si,1

ifndef NDEBUG
                inc     [si+callcount]
endif


;       Now the functions are called via our call-table;  all registers
;       contain their right values, except for SI and DS

                call    word ptr [si+call_table]
                cmp     word ptr [si+call_table],offset c_func
                je      c_exit

byebye:         pop     ds
                pop     si

byebye2:        iret

c_exit:         or      si,si
                jz      byebye

                pop     ds
                pop     si
                jmp     dword ptr cs:_old_int14



; ----------------------------------------------------------------------
;       Call C Functions
; ----------------------------------------------------------------------

c_func:         make_stack
                sti

                lea     ax,[bp-RAX]
                push    ss
                push    ax
                call    _c_interface
                add     sp,4

                cli
                mov     cs:foss_ax,ax
                unmake_stack
                mov     si,cs:foss_ax
                ret

foss_ax         dw      0



; ----------------------------------------------------------------------
;       Function 07h: Return System Timer Params
; ----------------------------------------------------------------------

get_sys_timer_p:mov     ax,01208h
                mov     dx,00037h

null_func:      ret



; ----------------------------------------------------------------------
;       Function 0Dh: Peek Ahead Keyboard
; ----------------------------------------------------------------------

peek_ahead_keyboard:
                mov     ah,1
                int     16h

                jnz     key_avail
                mov     ax,0ffffh
key_avail:      ret



; ----------------------------------------------------------------------
;       Function 0Eh: Read Keyboard
; ----------------------------------------------------------------------

read_keyboard:  mov     ah,0
                int     16h
                ret



; ----------------------------------------------------------------------
;       Function 11h: Set Cursor Position
; ----------------------------------------------------------------------

set_cursor_pos: mov     ah,2            ;set cursor pos
                mov     bh,0            ;page 0
                int     10h
                ret



; ----------------------------------------------------------------------
;       Function 12h: Get Cursor Position
; ----------------------------------------------------------------------

get_cursor_pos: mov     ah,3            ;get cursor pos
                mov     bh,0            ;page 0
                int     10h
                ret



; ----------------------------------------------------------------------
;       Function 13h: Write Char (ANSI support)
; ----------------------------------------------------------------------

write_char:     mov     dl,al
                mov     ah,2            ;disp char
                int     21h
                ret



; ----------------------------------------------------------------------
;       Function 15h: Fast Write Char (no ANSI support)
; ----------------------------------------------------------------------

fast_write_char:mov     ah,0eh          ;write only char to screen
                mov     bx,7            ;page/atb
                int     10h
                ret



; ----------------------------------------------------------------------
;       Function 16h: Ins/Del Function from timer chain
; ----------------------------------------------------------------------

maint_timer:    push    bx
                push    cx
                push    ds

                mov     bx,DGROUP
                mov     ds,bx

                mov     bx,offset timer_chain
                mov     cx,MAXTIMERHOOKS

                cmp     al,1
                je      ins_timer


;-------------- Delete function

timerd_next:    cmp     [bx],dx
                jne     timerd_notfound
                mov     ax,es
                cmp     [bx+2],ax
                jne     timerd_notfound

                xor     ax,ax
                cli
                mov     [bx],ax
                mov     [bx+2],ax
                sti

timer_success:  xor     ax,ax                   ;success
timer_ret:      pop     ds
                pop     cx
                pop     bx
                ret

timerd_notfound:add     bx,4
                loop    timerd_next

timer_fail:     mov     ax,0ffffh               ;fail
                jmp     short timer_ret


;-------------- Insert function

ins_timer:      mov     ax,[bx]
                or      ax,[bx+2]
                jnz     timeri_notempty

                cli
                mov     [bx],dx
                mov     [bx+2],es
                sti

                jmp     short timer_success

timeri_notempty:add     bx,4
                loop    ins_timer

                jmp     short timer_fail



; ----------------------------------------------------------------------
;       Function 17h: Reboot system
; ----------------------------------------------------------------------

;       On AL == 1 perform a warm boot, otherwise a warm boot.

public          _reboot

_reboot:        push    bp
                mov     bp,sp

                mov     ax,[bp+4]


reboot:         xor     bx,bx
                cmp     al,1
                jne     reboot_cold
                mov     bx,1234h

reboot_cold:    mov     ax,40h
                mov     ds,ax
                mov     ds:[72h],bx

                call    flush_caches

                push    sp
                pop     ax
                cmp     ax,sp
                jz      kill_286

;                jnz     kill_86
;                smsw    ax
;                test    al,1
;                jz      kill_286                ;only use out if not in VM

kill_86:        db      0eah,0f0h,0ffh,0,0f0h   ;jmp far f000:fff0

kill_286:       mov     al,0feh
                out     64h,al                  ;reset via keyboard ctrl
                jmp     short kill_86


;       The following code should take care of (hopefully) all
;       disk cache programs and flush there buffers BEFORE we
;       kill the box.  It's taken from the 80XXX echo.

flush_caches:
                mov     ah,0dh          ;DOS flush file buffers.
                int     21h             ;Most cache programs catch this call
                                        ;as well.

                ;Now call a cache dump for all cache programs I've found.
                ;Most are found in Ralf Brown's interrupt list.
                ;There has been some discussion whether all caches catch
                ;the DOS flush buffer or not, so to be safe we will force
                ;them all (at least all that are documented and that I can
                ;find).  The DOS flush buffer call should take care of most,
                ;if not all, in any case.

                mov     ah,21h          ;flush for QCASHE
                int     13h

                ;flush for  Super PC Kwik, PC Tools PC-Cashe 5.x,
                ;Qualitas Qcache 4.00

                mov     ah,0a1h
                mov     si,4358h
                int     13h

                mov     ax,0ffa5h       ;flush for PC-Cache v6+
                mov     cx,0ddddh
                int     16h

                push    ds
                xor     ax,ax
                mov     ds,ax
                mov     ax,ds:[4*2fh]
                or      ax,ds:[4*2fh+2]
                pop     ds
                jz      reboot_no_int2f         ; avoid 0:0 int 2f vector

                mov     ax,0fe03h       ;flush for Norton Utilities NCACHE
                mov     di,"NU"
                mov     si,"CF"
                int     2fh

                mov     ax,4a10h        ;flush for SMARTDRV v4.00+ -API
                mov     bx,0002h
                int     2fh

                call    HyperOff        ;flush hyperdisk 4.50+ caches

reboot_no_int2f:
                ret



; ----------------------------------------------------------------------
;       Turn off HyperDisk staged writes
; ----------------------------------------------------------------------

SearchHyAPINxt: inc     DGROUP:HyperCallOut     ; Disk Cache installed?
                jnz     short HyperOff          ; stop at 0FFh

SearchHyAPIRet: stc                             ; not found, carry Set
                ret


; ------------- Search and Init HyperDisk API

HyperOff:       xor     cx,cx                   ; zero cx
                xor     dx,dx                   ; zero dx
                mov     bx,HydkProductCode      ; Looking for HyperDisk

                mov     ah,DGROUP:HyperCallOut  ; Disk Cache installed?
                xor     al,al                   ; install check request

                push    ds                      ; make sure not changed
                int     2fh                     ; go do multiplex interrupt
                pop     ds

                or      al,al                   ; zero...no change
                je      short SearchHyAPIRet    ; nothing installed

                cmp     al,-1                   ; something installed is -1
                jne     short SearchHyAPINxt    ; try next Multiplex number if not -1

                cmp     cx,'YH'                 ; HyperWare Product?
                jne     short SearchHyAPINxt    ; if not skip to next Multiplex number

                or      dx,dx                   ; non-zero if HyperDisk installed
                jz      short SearchHyAPIRet    ; HyperDisk not here

                                                ; Found Product, Carry is clear
                                                ; AH = Multiplex #
                                                ; BX = CS segment of HyperDisk
                                                ; DX = HyperDisk Local Data Version #

;
; Get Current HyperDisk Cache State
;
; Calling Registers:
;
;       AH = Multiplex Number   ; Value determined by Install Check
;       AL = 01h                ; Get current HyperDisk Cache State
;       BX ='DH'                ; Product = Disk, HyperWare
;
;
; Return Registers:
;
;       AX = 0000h              ; function supported
;       BX = UsedBuffers        ; number of cache buffers in use
;       CX = ModifiedBuffers    ; number of modified buffers
;       DL = Current Cache values: 0 = Disabled, 1 = Enabled
;            Bit 0..StagedFloppy: Stage Write floppy drive write operations
;            Bit 1....StagedHard: Stage Write hard drive write operations
;            Bit 2..VerifyFloppy: Verify floppy drive write operations
;            Bit 3....VerifyHard: Verify hard drive write operations
;            Bit 4.....Reserved0: Reserved always 0
;            Bit 5.....Reserved1: Reserved always 0
;            Bit 6..FloppyEnable: Enable floppy caching
;            Bit 7..CacheEnabled: Enable all caching functions
;

;                mov     ah,DGROUP:HyperCallOut  ; HyperWare Multiplex Number
                mov     bx,HydkProductCode      ; HyperDisk product selector
                mov     al,01h                  ; Get current cache state

                int     2fh                     ; do multiplex interrupt

                or      ax,ax                   ; AX = 0, if supported
                jnz     HyperFail

                and     dl,11111100b            ; turn off staged writes

;
; Set HyperDisk Cache State, return previous Cache State
;
; Calling Registers:
;
;       AH = Multiplex Number   ; Value determined by Install Check
;       AL = 01h                ; Get current HyperDisk Cache State
;       BX ='DH'                ; Product = Disk, HyperWare
;       DL = New Cache values: 0 = Disabled, 1 = Enabled
;            Bit 0..StagedFloppy: Stage Write floppy drive write operations
;            Bit 1....StagedHard: Stage Write hard drive write operations
;            Bit 2..VerifyFloppy: Verify floppy drive write operations
;            Bit 3....VerifyHard: Verify hard drive write operations
;            Bit 4.....Reserved0: Reserved always 0
;            Bit 5.....Reserved1: Reserved always 0
;            Bit 6..FloppyEnable: Enable floppy caching
;            Bit 7..CacheEnabled: Enable all caching functions
;

HyperSet:       mov     ah,DGROUP:HyperCallOut  ; HyperWare Multiplex Number
                mov     bx,HydkProductCode      ; HyperDisk product selector
                mov     al,02h                  ; Set current cache state

                int     2fh                     ; do multiplex interrupt

                or      ax,ax                   ; AX = 0, if supported
                jnz     HyperFail

                mov     dh,0
                ret

HyperFail:      mov     dh,1
                ret



; ----------------------------------------------------------------------
;       Function 7Eh: Install User Appendage
; ----------------------------------------------------------------------

install_user:   push    ax

                cmp     al,80h
                jb      install_bad
                cmp     al,0bfh
                ja      install_bad
                cmp     al,APP_CODE
                je      install_bad

                mov     bh,0
                mov     bl,al
                sub     bl,080h
                add     bl,bl
                add     bl,bl
                add     bx,offset _TEXT:user_call_table

                cmp     ah,07fh
                je      uninstall_user

                mov     ax,cs:[bx]
                or      ax,cs:[bx+2]
                jnz     install_bad

                mov     cs:[bx],dx
                mov     cs:[bx+2],es

install_ok:     pop     bx
                mov     bh,1            ;success
install_bad2:   mov     ax,1954h
                jmp     byebye2

install_bad:    pop     bx
                mov     bh,0            ;fail
                jmp     install_bad2



; ----------------------------------------------------------------------
;       Function 7Fh: UnInstall User Appendage
; ----------------------------------------------------------------------

;       some stuff already done by install_user function above

uninstall_user: mov     ax,cs:[bx]
                or      ax,cs:[bx+2]
                jz      install_bad

uninstall_test_on:
                cmp     cs:[bx],dx
                jne     install_bad
                mov     ax,es
                cmp     cs:[bx+2],ax
                jne     install_bad

                xor     ax,ax
                mov     cs:[bx],ax
                mov     cs:[bx+2],ax
                jmp     short install_ok




; ----------------------------------------------------------------------
;       Own User Appendage
; ----------------------------------------------------------------------

public          _debug_funcs
extrn           _api_reregister:near
extrn           _timediff:dword

_debug_funcs:
                make_stack
                push    ax
                call    __debug_funcs
                pop     ax
                unmake_stack
                jmp     byebye2                 ;iret


__debug_funcs:
                push    bp
                mov     bp,sp
                push    si
                push    di

                mov     ax,[bp+4]

                cmp     al,0
                jnz     no_func_00


; ------------- Installation check

                mov     ax,offset _int14_entry
                mov     dx,cs

                jmp     short end_funcs

no_func_00:     cmp     al,1
                jne     no_func_01


; ------------- Reregister the application

                make_stack
                mov     ax,1
                push    ax
                call    _api_reregister
                pop     ax
                unmake_stack

                jmp     short end_funcs

no_func_01:     cmp     al,2
                jne     no_func_02


; ------------- Tranx request

                push    ds
                mov     ax,DGROUP
                mov     ds,ax

                mov     ax,word ptr DGROUP:_timediff[0]
                mov     dx,word ptr DGROUP:_timediff[2]

                push    ax
                xor     ax,ax
                mov     word ptr DGROUP:_timediff[0],ax
                mov     word ptr DGROUP:_timediff[2],ax
                pop     ax
                pop     ds

                jmp     short end_funcs

no_func_02:
ifndef  NDEBUG
                cmp     al,80h
                jnz     no_func_80


; ------------- DEBUG func: output function call count

                mov     si,offset callcount
                mov     cx,MAXFUNC

nx_func80:      push    cx
                lodsw
                push    ax
                mov     di,MAXFUNC
                sub     di,cx
                push    di
                mov     ax,offset msg_funccall
                push    ax

                call    _myaprintf
                add     sp,6
                pop     cx

                loop    nx_func80
                jmp     short end_funcs


; ------------- DEBUG func: zero callcount table

no_func_80:     cmp     al,81h
                jne     no_func_81

                push    cs
                pop     es

                mov     di,offset callcount
                mov     cx,MAXFUNC
                xor     ax,ax
                rep     stosw

                jmp     short end_funcs

no_func_81:
endif

end_funcs:      pop     di
                pop     si
                pop     bp
                ret



; ----------------------------------------------------------------------
;       Reentrant stack builder
; ----------------------------------------------------------------------

;           This function creates a stack, so C is content with the
;       small model limit SS == DS.  It was written to be reentrant, so
;       it may be used in an interrupt, even if it occured in our own
;       C funcs.
;           It is assumed that the interrupts are disabled before this
;       function is called.

build_stack:    cld
                                                        ;es:si+a  callret adr
                push    ax                              ;es:si+8
                push    bx                              ;es:si+6
                push    ds                              ;es:si+4
                push    es                              ;es:si+2
                push    si                              ;es:si

                mov     si,sp
                mov     bx,ss
                mov     es,bx

                mov     bx,DGROUP
                mov     ds,bx

                mov     bx,DGROUP:__next_stack          ;where to put the next stack
                cmp     bx,DGROUP:__stackbottom         ;lower than 1st stack?
                ja      build_stack_ok


;       The number of stacks has been exhausted;  now the stack grows into
;       the higher parts of the BSS and DATA segments and trashes data.
;       Display an error message and get ready to reboot.

                call    beep
                mov     di,offset DGROUP:msg_2manystacks
print_and_boot: call    bios_disp

pab_nx_key:     mov     ah,0
                int     16h
                cmp     ax,4400h                ;was F10?
                jne     pab_nx_key

                mov     al,0
                jmp     reboot


build_stack_ok: mov     [bx-4],sp                       ;to the bottom of the
                mov     [bx-2],ss                       ;new stack
                sub     DGROUP:__next_stack,STACK_LEN

                mov     ax,ds
                mov     ss,ax
                lea     sp,[bx-4]

                push    bp                              ;build stack frame
                mov     bp,sp

                mov     [bp-(STACK_LEN-6-2)],STACK_SIGN

                mov     ax,es:[si+8]            ;ax
                push    ax
                mov     bx,es:[si+6]            ;bx
                push    bx
                push    cx
                push    dx
                push    es:[si]                 ;si
                push    di
                push    es:[si+4]               ;ds
                push    es:[si+2]               ;es

                jmp     word ptr es:[si+10]             ;get back




; ----------------------------------------------------------------------
;       Reentrant stack rebuilder
; ----------------------------------------------------------------------

rebuild_stack:

                mov     ax,DGROUP
                mov     ds,ax
                add     DGROUP:__next_stack,STACK_LEN

                mov     bp,sp
                cmp     [bp-(STACK_LEN-6-2)+9*2],STACK_SIGN
                je      rebuild_stack_ok

;       Obviously the stacks have overflowed;  this should not occure
;       and is a very severe error.  Please contact the authors;  to
;       avoid future problems try increasing the stacksize in units of
;       256 bytes.

                call    beep
                mov     di,offset DGROUP:msg_stkoverfl
                jmp     short print_and_boot

rebuild_stack_ok:
                mov     bp,sp
                mov     es,[bp+22]      ;old SS
                mov     si,[bp+20]      ;old SP

                pop     es:[si+10]      ;callret
                pop     es:[si+2]       ;es
                pop     es:[si+4]       ;ds
                pop     di
                pop     es:[si]         ;si
                pop     dx
                pop     cx
                pop     es:[si+6]       ;bx
                pop     es:[si+8]       ;ax

                pop     bp

                mov     bx,sp
                mov     ax,ss:[bx+2]
                mov     sp,ss:[bx]
                mov     ss,ax

                pop     si
                pop     es
                pop     ds
                pop     bx
                pop     ax

                ret



;       Like build_stack, this func assumes/requires that the interrupts
;       are disabled.




; ----------------------------------------------------------------------
;       386 aware memcpy() function
; ----------------------------------------------------------------------

public          MEMCPY3

MEMCPY3         proc    far
                mov     bx,sp
                push    si
                push    di
                push    ds

                cld
                mov     cx,ss:[bx+4]                    ;len
                les     di,dword ptr ss:[bx+10]         ;dest
                lds     si,dword ptr ss:[bx+6]          ;src

                mov     dx,es
                mov     ax,di
                jcxz    nomore

                cmp     cx,10h                          ;big enough?
                jae     big_enough
                jmp     short memcpy86

dword_bound_it: movsb
                dec     cx
big_enough:     test    si,3                            ;4*x+3 bound?
                jnz     dword_bound_it

no386?:         jmp     short memcpy86                  ;is patched by INIT3


; ------------- 386 move

                mov     bx,di
                and     bl,3
                cmp     bl,2
                je      memcpy86

                push    cx

                .386
                shr     cx,2
                repz    movsd
                .8086

                pop     cx
                and     cx,3
                jmp     short last_bytes


; ------------- 8086 move

memcpy86:       shr     cx,1
                rep     movsw
                rcl     cx,1

last_bytes:     rep     movsb

nomore:         pop     ds
                pop     di
                pop     si

                ret     2+4+4
MEMCPY3         endp



; ----------------------------------------------------------------------
;       Interrupt functions
; ----------------------------------------------------------------------

;struct         WORDREGS {unsigned ax,bx,cx,dx,si,di,cflag,flags; };
_ax             equ     0
_bx             equ     2
_cx             equ     4
_dx             equ     6
_si             equ     8
_di             equ     10
_cflag          equ     12
_flags          equ     14

;struct         SREGS { unsigned es,cs,ss,ds; };
_es             equ     0
_cs             equ     2
_ss             equ     4
_ds             equ     6


LCODE   equ     1

arg1    equ     [bp+4+2*LCODE]
arg2    equ     [bp+6+2*LCODE]
arg3    equ     [bp+8+2*LCODE]
arg4    equ     [bp+10+2*LCODE]
arg5    equ     [bp+12+2*LCODE]
arg6    equ     [bp+14+2*LCODE]
arg7    equ     [bp+16+2*LCODE]


public          _myint86x
_myint86x       proc    far

                push    bp
                mov     bp,sp

                push    si
                push    di
                push    ds

                push    bp

                pushf
                push    cs
                push    cs:retval

                les     bx,arg2
                mov     ax,es:[bx]._flags

;                          7654321.7654321.
                and     ax,0000100011010101b
                push    ax

                xor     bx,bx
                mov     es,bx
                mov     bx,arg1                 ;intno
                add     bx,bx
                add     bx,bx
                les     bx,es:[bx]

                push    es
                push    bx

                les     bx,arg6                 ;segregs
                mov     ds,es:[bx]._ds
                mov     ax,es:[bx]._es
                push    ax

                les     bx,arg2                 ;inregs
                mov     ax,es:[bx]._bx
                push    ax
                mov     ax,es:[bx]._ax
                mov     cx,es:[bx]._cx
                mov     dx,es:[bx]._dx
                mov     si,es:[bx]._si
                mov     di,es:[bx]._di

                pop     bx
                pop     es

                cli
                iret

retval          dw      offset go_here

go_here:        pop     bp

                push    es
                pushf
                push    bx

                les     bx,arg4                 ;outregs
                mov     es:[bx]._ax,ax
                mov     es:[bx]._cx,cx
                mov     es:[bx]._dx,dx
                mov     es:[bx]._si,si
                mov     es:[bx]._di,di
                pop     es:[bx]._bx
                pop     cx
                mov     es:[bx]._flags,cx

                xor     dx,dx
                test    cl,1
                jz      myint_nocf
                mov     dl,1
myint_nocf:     mov     es:[bx]._cflag,dx

                les     bx,arg6                 ;segregs
                mov     es:[bx]._cs,cs
                mov     es:[bx]._ds,ds
                mov     es:[bx]._ss,ss
                pop     es:[bx]._es

                pop     ds
                pop     di
                pop     si
                pop     bp

                ret                             ;return value is AX

_myint86x       endp



public          _mysegread
_mysegread      proc    far

                push    bp
                mov     bp,sp

                mov     ax,es

                les     bx,arg1
                mov     es:[bx]._cs,cs
                mov     es:[bx]._ds,ds
                mov     es:[bx]._es,ax
                mov     es:[bx]._ss,ss

                pop     bp
                ret

_mysegread      endp



public          _mycallf
_mycallf        proc    far

                push    bp
                mov     bp,sp

                push    si
                push    di
                push    ds

                push    bp

                push    cs
                push    cs:retval2

                les     bx,arg3
                mov     ax,es:[bx]._flags

;                          7654321.7654321.
                and     ax,0000100011010101b
                push    ax

                push    arg2
                push    arg1                    ;call dest

                les     bx,arg7                 ;segregs
                mov     ds,es:[bx]._ds
                mov     ax,es:[bx]._es
                push    ax

                les     bx,arg3                 ;inregs
                mov     ax,es:[bx]._bx
                push    ax
                mov     ax,es:[bx]._ax
                mov     cx,es:[bx]._cx
                mov     dx,es:[bx]._dx
                mov     si,es:[bx]._si
                mov     di,es:[bx]._di

                pop     bx
                pop     es

                cli
                iret

retval2         dw      offset go_here2

go_here2:       pop     bp

                push    es
                pushf
                push    bx

                les     bx,arg5                 ;outregs
                mov     es:[bx]._ax,ax
                mov     es:[bx]._cx,cx
                mov     es:[bx]._dx,dx
                mov     es:[bx]._si,si
                mov     es:[bx]._di,di
                pop     es:[bx]._bx
                pop     cx
                mov     es:[bx]._flags,cx

                xor     dx,dx
                test    cl,1
                jz      myint_nocf2
                mov     dl,1
myint_nocf2:    mov     es:[bx]._cflag,dx

                les     bx,arg7                 ;segregs
                mov     es:[bx]._cs,cs
                mov     es:[bx]._ds,ds
                mov     es:[bx]._ss,ss
                pop     es:[bx]._es

                pop     ds
                pop     di
                pop     si
                pop     bp

                ret                             ;return value is AX

_mycallf        endp





if 0
public          _exit
_exit           proc    near
                mov     bp,sp
                mov     ax,[bp+4]
                mov     ah,4ch
                int     21h
_exit           endp
endif


;; ------------- allocate memory
;
;public          _dos_malloc
;_dos_malloc:    push    bp
;                mov     bp,sp
;                push    cx
;
;                mov     ah,48h                  ;malloc
;                mov     bx,[bp+4]               ;DX:BX = size in bytes
;                mov     dx,[bp+6]
;                add     bx,15
;                adc     dx,0
;
;                mov     cx,4                    ;shift 4bits right
;malloc_lop:     shr     dx,1
;                rcr     bx,1
;                loop    malloc_lop
;
;                int     21h                     ;alloc memory
;                jc      malloc_nomem
;
;                mov     dx,ax
;                jmp     short malloc_ret
;
;malloc_nomem:   xor     dx,dx
;malloc_ret:     xor     ax,ax
;
;                pop     cx
;                pop     bp
;                ret


; ----------------------------------------------------------------------
;       TELES specific CAPI functions
;
;       Thanx to Andreas Illg from TELES GmbH for
;       providing us with this code
; ----------------------------------------------------------------------

;       slight TASM repair actions ;-)

;%NOCTLS
;%NOINCL
;%NOMACS
;%NOCONDS

;      .MODEL SMALL, C
;      MASM51
;      LOCALS
;      .XALL

; Definition eines globalen Symbols
PBSYM  MACRO name, rest
PUBLIC name
&name&rest
ENDM

;.CODE

;==============================================================================

; Fuer den Multiplex-Interrupt 0x2F
MIF_KERNEL          = 0C9h      ; Interrupthandler fuer den Multiplexinterrupt
MIK_GETKERNELENTRY  = 001h      ; lesen des Kerneleinsprungs

;==============================================================================

KE_OK                       =     0
KE_KERNEL_NOT_PRESENT       =    -2

;==============================================================================

F_PROVIDER          = 15

;==============================================================================

PBSYM _kernel_entry < DD ?>

PBSYM _init_kerneluse < PROC>
;      USES bx, cx, dx, di, si, es, ds

      push  bx
      push  cx
      push  dx
      push  di
      push  si
      push  es
      push  ds

      ; Feststellen des Kerneleinsprungs ueber den Multiplex-Interrupt
      mov   ah, MIF_KERNEL
      mov   al, MIK_GETKERNELENTRY ;
      xor   dx, dx
      mov   es, dx
      int   02fh
      mov   ax, es
      or    ax, ax
      jz    @@no_kernel
      mov   cs:[WORD PTR _kernel_entry], dx    ; Offset  des Kerneleinsprungs
      mov   cs:[WORD PTR _kernel_entry+2], ax  ; Segment des Kerneleinsprungs
      mov   ax, KE_OK

@@return:
      pop   ds
      pop   es
      pop   si
      pop   di
      pop   dx
      pop   cx
      pop   bx
      ret

@@no_kernel:
      mov   ax, KE_KERNEL_NOT_PRESENT
      jmp   short @@return
_init_kerneluse ENDP

;==============================================================================

PBSYM _provider < PROC>
;      ARG   service : DWORD    ; Far-Zeiger auf den Namen des Dienstes

      push  bp
      mov   bp,sp

      mov   ax, F_PROVIDER
      call  dword ptr cs:[_kernel_entry]

      pop   bp
      ret
_provider ENDP

;==============================================================================


; ------------- Warn if not CLI  --  DEBUG

ifndef  NDEBUG
public          _check_cli
_check_cli:     push    ax
                pushf
                pop     ax
                test    ah,10b
                pop     ax
                jz      check_cli_ok

                mov     ax,offset msg_nocli
                push    ax
                call    _myaprintf
                pop     ax

check_cli_ok:   ret
endif


; ------------- Get CPU flags  --  DEBUG

ifndef NDEBUG
public          _get_flags
_get_flags:     pushf
                pop     ax
                ret
endif


; ------------- Clear Interrupts and return old val

public          _mycli
_mycli:         pushf
                pop     ax
                cli
                and     ax,0200h
                ret


; ------------- Set Interrupts and return old val

public          _mysti
_mysti:         pushf
                pop     ax
                sti
                and     ax,0200h
                ret


; ------------- Set/Clear Interrupts

public          _myseti
_myseti:        push    bp
                mov     bp,sp

                mov     ax,[bp+4]
                test    ah,2
                jz      myseti_c

                sti
myseti_end:     pop     bp
                ret

myseti_c:       cli
                jmp     short myseti_end


; ------------- BEEEEP

beep:           push    ax
                push    bx
                mov     ax,0e07h
                mov     bx,7
                int     10h
                pop     bx
                pop     ax
                ret


; ------------- BIOS disp

bios_disp:      push    di
                mov     cx,DGROUP
                mov     es,cx
                mov     cx,0ffffh               ;inline strlen ()
                xor     al,al
                repne   scasb
                not     cx
                dec     cx
                pop     bp

                mov     ax,1301h
                mov     bx,7
                mov     dx,0
                int     10h

                ret


; ------------- C like disp function

c_disp:         lodsb
                or      al,al
                jz      c_disp_end
                mov     dl,al
                mov     ah,2
                int     21h
                jmp     short c_disp

c_disp_end:     ret


_TEXT           ends



; ----------------------------------------------------------------------
;       Data Definitions
; ----------------------------------------------------------------------

public          __osver
public          __osmajor
public          __osminor
public          __psp
public          _aenvseg
public          __acrtused
;public          _use_386
public          __next_stack
public          __stackbottom
public          __indos
public          __stack_size
public          __stack_num
public          __vsnnum
public          _ascii_id
public          _numticks
public          __timerval
public          _banner1
public          _banner2


_DATA           segment word public 'DATA'

HyperCallOut    db      0DFh            ; Dos MultiPlex Number for HyperWare

_ascii_id       db      _NAME,' ',_VSN,'; ISDN CAPI MultiPort FOSSIL by Zaphods BBS',0

banner          db      13,10,'   '
_banner1        db      _NAME,' ',_VSN,' -- ISDN CAPI MultiPort FOSSIL driver',0
banner2         db      13,10,'   '
_banner2        db      'Copyright (c) 1993 Martin Winkler, Chris Lueders'
                db      13,10,13,10,0

ifndef  NDEBUG
msg_nocli       db      "Interrupts were enabled (unexpected)", 10, 0
msg_funccall    db      "func %02x called %5u times", 10, 0

callcount       dw      MAXFUNC dup (0)
endif

msg_2manystacks db      "Stacks exhausted; Press F10 to reboot.. ", 0
msg_stkoverfl   db      "Stack overflowed or corrupted; Press F10 to reboot.. ", 0

call_table      dw      offset c_func                   ;00
                dw      offset c_func                   ;01
                dw      offset c_func                   ;02
                dw      offset c_func                   ;03
                dw      offset c_func                   ;04
                dw      offset c_func                   ;05
                dw      offset c_func                   ;06
                dw      offset get_sys_timer_p          ;07
                dw      offset c_func                   ;08
                dw      offset c_func                   ;09
                dw      offset c_func                   ;0a
                dw      offset c_func                   ;0b
                dw      offset c_func                   ;0c
                dw      offset peek_ahead_keyboard      ;0d
                dw      offset read_keyboard            ;0e
                dw      offset null_func                ;0f
                dw      offset c_func                   ;10
                dw      offset set_cursor_pos           ;11
                dw      offset get_cursor_pos           ;12
                dw      offset write_char               ;13
                dw      offset c_func                   ;14
                dw      offset fast_write_char          ;15
                dw      offset maint_timer              ;16
                dw      offset reboot                   ;17
                dw      offset c_func                   ;18
                dw      offset c_func                   ;19
                dw      offset null_func                ;1a  ;send break
                dw      offset c_func                   ;1b

user_call_table dd      0bfh-080h+1 dup (0)             ;user appendages
timer_chain     dd      MAXTIMERHOOKS dup (0)           ;timer chain hooks

__timerval      label   dword
timer_lo        dw      0
timer_hi        dw      0
__osver         label   word
__osmajor       db      0
__osminor       db      0
__psp           dw      0
_aenvseg        dw      0
__acrtused      dw      0                       ;what's that?
;_use_386        db      0
__next_stack    dw      0                       ;is set by _main()
__stackbottom   dw      0
__indos         dd      0
__stack_size    dw      STACK_LEN
__stack_num     dw      STACK_MAX
__vsnnum        db      _VSNNUM
_numticks       dw      0

_DATA           ends


                end     get_going
