/***
*dbgstk.c - debug check stack routine
*
*       Copyright (c) 1986-1995, Microsoft Corporation. All rights reserved.
*
*Purpose:
*   This module contains a debug impelmentation of the standard _chkstk
*   for i386.  It will do the standard stack probe (code copied from
*   VC5 CRT) and then call a debug routine which will have the oportunity
*   top spew the stack before it gets initialized (or not).
*
*******************************************************************************/
#include "headers.hxx"

#if defined(USE_STACK_SPEW) && defined(_X86_)

#pragma check_stack(off)

static BOOL    g_fStackSpewEnabled = FALSE;
static DWORD   g_dwSpew = 0x0;

extern "C" void __declspec(naked) __cdecl _chkstk()
{
    _asm
    {
        ;  First probe the stack.  We do this because
        ;  we don't want to write past the stack guard page
        ;  Note that this code came from the original
        ;  c run time source.

        push    ecx                     ; save ecx
        push    eax                     ; save eax (size of stack needed)
        cmp     eax,1000h               ; more than one page requested?
        lea     ecx,[esp] + 12          ;   compute new stack pointer in ecx
                                        ;   correct for return address and
                                        ;   saved ecx, eax
        jb      short lastpage          ; no

probepages:
        sub     ecx,1000h               ; yes, move down a page
        sub     eax,1000h               ; adjust request and...

        test    dword ptr [ecx],eax     ; ...probe it

        cmp     eax,1000h               ; more than one page requested?
        jae     short probepages        ; no

lastpage:
        sub     ecx,eax                 ; move stack down by eax
        mov     eax,esp                 ; save current tos and do a...

        test    dword ptr [ecx],eax     ; ...probe in case a page was crossed

        ;  Now set up and write our data into the area of the stack
        ;  that was opened up

        lea     esp,[ecx] - 12          ; set the stack pointer to the bottom
                                        ; leave room 12 in padding so we don't
                                        ; clobber ourselves

        mov     ecx,dword ptr [eax+8]   ; recover return address
        push    ecx

        cmp     g_fStackSpewEnabled,0   ; see if we are enabled

        mov     ecx,dword ptr [eax+4]   ; recover original ecx

        je      done                    ; not enabled

        push    ecx                     ; save original ecx

        pushfd                          ; save flags
        std                             ; set DI: decr edi after stosd

        mov     ecx,dword ptr [eax]     ; recover original eax (stack size)

        push    edi                     ; save edi on stack also
        lea     edi,[eax]+8             ; load up iterator start address

        shr     ecx,2                   ; get count of dwords

        mov     eax,g_dwSpew            ; load up value

        rep stosd                       ; let 'er rip

        pop     edi                     ; pop saved edi
        popfd                           ; pop flags
        pop     ecx                     ; pop saved ecx

done:
        ret     12                      ; return, popping off 12 padding
    }
}

// NOTE: _alloca_probe is impelemented exactly the same as _chkstk
// I'd like to find some way to merge these two pieces of code but I
// don't know how with inline assembly...
extern "C" void __declspec(naked) __cdecl _alloca_probe()
{
    _asm
    {
        ;  First probe the stack.  We do this because
        ;  we don't want to write past the stack guard page
        ;  Note that this code came from the original
        ;  c run time source.

        push    ecx                     ; save ecx
        push    eax                     ; save eax (size of stack needed)
        cmp     eax,1000h               ; more than one page requested?
        lea     ecx,[esp] + 12          ;   compute new stack pointer in ecx
                                        ;   correct for return address and
                                        ;   saved ecx, eax
        jb      short lastpage          ; no

probepages:
        sub     ecx,1000h               ; yes, move down a page
        sub     eax,1000h               ; adjust request and...

        test    dword ptr [ecx],eax     ; ...probe it

        cmp     eax,1000h               ; more than one page requested?
        jae     short probepages        ; no

lastpage:
        sub     ecx,eax                 ; move stack down by eax
        mov     eax,esp                 ; save current tos and do a...

        test    dword ptr [ecx],eax     ; ...probe in case a page was crossed

        ;  Now set up and write our data into the area of the stack
        ;  that was opened up

        lea     esp,[ecx] - 12          ; set the stack pointer to the bottom
                                        ; leave room 12 in padding so we don't
                                        ; clobber ourselves

        mov     ecx,dword ptr [eax+8]   ; recover return address
        push    ecx

        cmp     g_fStackSpewEnabled,0   ; see if we are enabled

        mov     ecx,dword ptr [eax+4]   ; recover original ecx

        je      done                    ; not enabled

        push    ecx                     ; save original ecx

        pushfd                          ; save flags
        std                             ; set DI: decr edi after stosd

        mov     ecx,dword ptr [eax]     ; recover original eax (stack size)

        push    edi                     ; save edi on stack also
        lea     edi,[eax]+8             ; load up iterator start address

        shr     ecx,2                   ; get count of dwords

        mov     eax,g_dwSpew            ; load up value

        rep stosd                       ; let 'er rip

        pop     edi                     ; pop saved edi
        popfd                           ; pop flags
        pop     ecx                     ; pop saved ecx

done:
        ret     12                      ; return, popping off 12 padding
    }
}

//
// Initialize the debug stack system
//

extern "C" void
InitChkStk(BOOL dwFill)
{
    g_dwSpew = dwFill;
    g_fStackSpewEnabled = TRUE;
}

#endif