/*
    TMHalAsm.S, Copyright  (c) by Lukas Ruf, lr@lpr.ch,
    Swiss Federal Institute of Technology,
    Computer Engineering and Networks Laboratory.

    TOPSY -- A Teachable Operating System.
             Implementation of a tiny and simple
             micro kernel for teaching purposes.

    For further information, please visit http://www.tik.ee.ethz.ch/~topsy

    This software is provided under the terms of the GNU General Public Licence.

    A full copy of the GNU GPL is provided in the file COPYING found in the 
    development root of Topsy.
*/
/*
    File:                  $Source: /home/ruf/cvs_Topsy/cvs/Topsy/Threads/ia32/TMHalAsm.S,v $
    Author(s):             Lukas Ruf, lr@lpr.ch
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 3041 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-06-19 22:29:34 +0200 (Die, 19 Jun 2007) $      by: $Author: sryffel $
*/
#include "asm.h"
#include "cpu.h"
#include "MemoryLayout.h"
#include "SyscallMsg.h"         
//-LR #include "System.h"         

/* define SASM to make only "things" for assembler visible */
#define __SASM__
#include "TMHalAsm.h"
#include "Exception.h"
#ifdef APIC
#include "TMHalApic.h"
#endif
#undef __SASM__

/* Definitions for Interrupt Handling.
 */
#define TRAP(_irqno_) \
        pushal ; \
        nop ; \
        pushl $(_irqno_) ; \
        jmp __general_ExceptionHandler

#define IRQ(_name_,_irqno_) \
        .align 4 ; \
        .globl _name_ ; _name_: ; \
        cli ; \
        pushl $0 ; \
        TRAP(_irqno_)

/* Definitions for Execption Handling.
 */
/* Define the entry for IDT Vectorization :-)
   Please remark: as some exception serve with an error code, there is no
   generalisation possible :-(
 */     
#define IDTVEC(_name_) \
        .align 4 ; \
        .globl _name_ ; _name_: \
        cli
        
.extern _INTHandler

/* General constants */
#define BIT_UNSET 0
#define BIT_SET   1
  
#define GOTO_HEAVEN_OR_HELL     iret

        .section .sasm
        .align 4,0x9090
 
    /* Save context of current thread. All registers but k0, k1, AT and
     * zero are saved in the ProcContext structure given by the first
     * argument to saveContext(), thus register a0. 
     * ASM_PROC_FRAME(saveContext) !!!!!***i386***!!!!!
     * Note:SaveContext is in i386 implicitely called by the low level IRQ-Handlers.
     */

/* fields of the Thread structure */
#define TH_CTX  %ss:( 0*4)
#define TH_STAT %ss:(52*4)
#define TH_PRIO %ss:(53*4)

/* fields of the stack frame */
#define SF_LAST       %ss:(0*4)
#define SF_PRIO       %ss:(1*4)
#define SF_STAT       %ss:(2*4)
#define SF_PDBR       %ss:(7*4)
#define SF_ERR        %ss:(17*4)
#define SF_EIP        %ss:(18*4)
#define SF_CS         %ss:(19*4)
#define SF_EFLAGS     %ss:(20*4)
#define SF_ESP        %ss:(21*4)

/* Fields of the context */
#define CTX_SF         %ss:(0*4)
#define CTX_TSS        %ss:(1*4)
#define CTX_ESP0       %ss:(2*4)
#define CTX_SS0        %ss:(3*4)

/**__restoreContext()**********************************************************/
    
    /* void __restoreContext(ProcContextPtr contextPtr);
     *  restore CPU State given by this Stack Frame 
     *  we are in the right vm already
     *
     */
    
ASM_PROC_FRAME(__restoreContext)
    ENTER 

    movl    ARG1,%esi             /* store the address of the context in esi     */
    movl    CTX_SF(%esi), %edi    /* store the address of the stack frame in edi */


    movl    SF_LAST(%edi), %eax   
    movl    %eax, CTX_SF(%esi)    /* restore the old Stack Frame pointer         */


    movl    %edi,%esp             /* set the ESP to the context to be restored   */

    addl    $12, %esp             /* remove values for: last, priority and schedFlags*/

    /* restore selectors */
    popl    %gs             /* General Purpose Segment Selector */
    popl    %fs             /* Further Purpose Segment Selector */
    popl    %es             /* Extra Segment Selector */
    popl    %ds             /* Data  Segment Selector */
    
    popl    %eax            /* pop PDBR                    */
    movl    %eax,%cr3       /* move to the new PDBR       */
    addl    $4,%esp         /* remove IRQ no. from stack  */

    popal                   /* restore all registers      */

    addl    $4,%esp         /* remove the error code      */

    iret                    /* return from interrupt      */


#if 0   // old code implementing a stack change
    movl    _CS(%edi), %eax /* move cs to eax                      */
    andl    $0x3, %eax      /* extract rpl of the segment selector */
    je      restore_kernel  /* rpl is  0 -> kernel                 */
    jmp     restore_user    /* rpl is >0 -> "user"                 */

    // subl    $0x3, %eax      /* check rpl of code segment selector  */
    // je      restore_user    /* rpl is  3 -> user   */
    // jmp     restore_kernel  /* rpl is <3 -> kernel */
    

restore_user:
                              /* esp now points to tf_edi              */
    popal                     /* restore all general purpose registers */
                              /* esp now points to tf_err              */
    iret                      /* nothing more to do, since iret will   */
                              /*  pop the new esp and ss from the stack*/ 
    
restore_kernel:
    /* Copy the values to the new stack and execute iret from there.
     *  the new stack sould look like this:
     *    +--------------+
     *    |              | <- after iret, esp points here
     *    | EFlags       |
     *    | CS           |
     *    | EIP          | <- when executing iret, esp should point here
     *    +--------------+
     */

    movl    _ESP(%edi), %ebx    /* copy Stack Address to ebx */
    subl    $12, %ebx           /* make room for data        */
    movl    _EIP(%edi), %eax    /* copy EIP                  */
    movl    %eax, %ss:(%ebx) 
    movl    _CS(%edi), %eax     /* copy CS                   */
    movl    %eax, %ss:4(%ebx)  
    movl    _EFLAGS(%edi), %eax /* copy EFlags               */
    movl    %eax, %ss:8(%ebx)

    movl    %ebx, %ss:xtemp     /* save esp, ss is the Kernel Data Segment */

    popal                       /* restore all general purpose registers   */

    movl    %ss:xtemp, %esp     /* move to the new stack */

    iret                        /* Return from interrupt */
#endif  


/**End of __restoreContext()***************************************************/


/**restoreContext()************************************************************/

    /* LR 000701 */
    /* Review of restoreContext() due to debugging. */
    

    /* restoreContext(ProcContextPtr Context);
     * Ideally this should be called FAKE STACK. Because in i386 environment
     * the context of a thread is restored by modifying the stack.
     * Forget all register savings. Proceed as even you were alone on the world..
     * Do not take care about stack environment.... it is all saved on the
     * kernel stack via pushall ... so load the stack pointer from Context.tf_xsp
     * and launch a popall... afterwards continue with an iret (interrupt return)
     *
     */ 
    ASM_PROC_FRAME(restoreContext_old)
        ENTER   /* Execute an ENTER to Procedure entry as to provide a stack
                 * frame as common in C/Topsy-Assembler
                 */
        /* the kernel stack includes all flags and common purpose registers */
        cli      /* disable interrupt execution -- we modify 
                    the stack directly. Even if Interrupts are 
                    turned of, make this sure */
                                   
        /* do some statistics when restoreContext is called */
        call    SysRestored
        
        movl    ARG1,%eax       /* store the address of ProcContext in eax */
        
//-LR         pushl   %eax            /* pass the address of the failed context-location */
//-LR         call    DumpStack
//-LR         call    BRK
//-LR         popl    %eax
#if 0
        /* Verify the location of the current ProcContext: Check by that
         * wether a pointer/memory override occured
         */

        /* Set a lower bound for the current context: very small kernels
         * may run into troubles (< 32KB)
         */
//-CJ        cmpl    $0x8000,%eax            /* Kernel Code */
		cmpl     $0xE0000,%eax         /* 1MB - 128kB */
		/* The kernel space is now set form 1MB-2MB so 
        jna      __lowerBoundFailed     /* Jump If Not Above */

//-LR         call BRK
        
        /* Set upper bound for the current context: below 640KB
         * relativ to start of kernel segments
         * (0xA00000 - 0x20000 = 0x80000)
         * (640KB    - 128KB   = 512KB)
         */
        /* WARNING: THIS IS HARDCODED WITH RESPECT TO THE MEMORY LAYOUT */
//-CJ        cmpl    $0x80000,%eax           /* Begin of IO-Region */
        cmpl    $0x1E0000,%eax           /* Begin of User-Region */
        jna     __contextBoundariesOK   /* Jump If Not Aove */

//-LR         call BRK
        
        /* If the address of the Processor Context failed 
         * Abort by a stack dump */
     __lowerBoundFailed: 

        /* If the verfification of the segment selectors failed 
         */
     __segmentRegisterCheckFailed:
//-LR         movl    $BOOTSTACKTOP,%esp /* set exception stack by hand */
        /* We do not need to adjust the stack pointer: we are still running
         * in the kernel environment and by that have a correctly set stack
         * pointer.
         */
        pushl   %eax            /* pass the address of the failed context-location */
        call    DumpStack
        hlt                     /* and abort processing */
#endif       
    __contextBoundariesOK:
        /* Verify the important segment registers for validity: this is
         * heavily configuration dependent.
         */

        /* adjust the EBP for checks with the macros as defined in 
         * Topsy/ia32/asm.h
         */
//-LR         call BRK
        
//-LR         movl    %eax,%ebp
//-LR         VERIFY(GS) ; jnz __segmentRegisterCheckFailed
//-LR         VERIFY(FS) ; jnz __segmentRegisterCheckFailed
//-LR         VERIFY(ES) ; jnz __segmentRegisterCheckFailed
//-LR         VERIFY(DS) ; jnz __segmentRegisterCheckFailed

//-LR         VERIFY(CS) ; jnz __segmentRegisterCheckFailed
//-LR         VERIFY(SS) ; jnz __segmentRegisterCheckFailed

        /* Now:
         * - the segment selectors are 0x08-Byte boundary-aligned.
         * - the context pointer is valid with kernels smaller 512KB - 64KB.
         */
#if 0 
        /* Before changing %esp, we have to make sure, that no Page Fault
         * occures afterwards. Therefore we write some random data to the
         * target stack.
         */
        movl    72(%eax), %edi            /* edi = Context->tf_esp;  18*4=72   */
        movl    $0x12345678, -12(%edi)    /* random data to the last position  */
#endif
        /* become magic:
         * Set the stack pointer to the context to be restored.
         * Drop that context by restoring it and by that jump to the process/
         * thread to be restored.
         */

        movl    %eax,%esp       /* set the ESP to the context to be restored */

        /* restore selectors */
        popl    %gs             /* General Purpose Segment Selector */
        popl    %fs             /* Further Purpose Segment Selector */
        popl    %es             /* Extra Segment Selector */
        popl    %ds             /* Data  Segment Selector */
       
        /* restore Page Directory Base Register */
        popl    %eax
        movl    %eax,%cr3

        /* ESP points now to the trapno */
        
        addl    $4,%esp         /* remove IRQ no. from stack  */

        popal                   /* restore all general purpose registers */
        /* Now:
         * - all general purpose registers are restored.
         * - the macros of Topsy/ia32/asm.h cannot be applied anymore
         * - only ESP, CS and SS are still unchanged 
         */

        addl    $4,%esp         /* remove err no. too */

        /* ########################### */       
        /* NOTE: ESP points now to EIP */
        /* ########################### */       
        /* Become real magician */
        
        /* Assumption: Right above, we did not modify the process context
         * but kept it, as it was provided at restoreContext-calling time.
         * We have not wrote any data into the process context.
         */
        
        /* Process the target CS: if it is equal to the kernel code selector,
         * no PRL (protection ring level) crossing took place.  In this case,
         * the USER ESP and USER SS are invalid.  Otherwise, they are used for 
         * changing the rings -- am I lord of the rings now :-) ?
         *
         * IMPORTANT:
         * If other code segments running at CPL (current protection level) 0,
         * the modifier must take care of the now following code !!!
         */

        /* ESP still points to EIP :-)
         * Above *ESP, CS is located.
         */
        cmpl    $GC_KCSEL, 4(%esp)  /* compare the provided Code Selector */
        jne     __PRLChange        /* jump if PRL-change takes place */
        
        /* So, we are about to start a PRL change ! */
//-LR         This might be superflucious, check the exact semantic of popal !
//-LR         pushl   $GC_KDSEL        /* make sure the stack segment equals to the */
//-LR         popl    %ss             /* kernel data segment */

        /* ESP still points to EIP :-) */

//-LR #error ###############################################################
//-LR #error Exact Semantics of PUSHALL/POPALL in case of PRL change or not.
//-LR #error It is actually unclear to me why the EIP, CS and EFLAGS need to 
//-LR #error be transfered from the current processor context into the new
//-LR #error thread's one when the PRL is to be changed.
//-LR #error Think of adjusting the new exception stack pointer to be point-
//-LR #error ing directly to the currently running processor context frame.
//-LR #error ###############################################################
        
        
        /* Now, we need to transfer the ??? */
         
        /* save %edi to xtemp: no mutex or semaphore is required as all ints
           are disabled --> directly acces all */
        movl    %edi,%ss:xtemp  /* note: here we are in kernel data segment ! */
        movl    12(%esp),%edi   /* %edi:= tf_esp */
        
        /* make room for the temporarely setting of EIP, CS and EFLAGS on the
           stack of the thread to be started */
        subl    $12,%edi

        /* use %eax to transfer: this f...ing gas does not understand all newely
           implemented features of the ix86-architecture as memory to memory moves */
        movl    %eax,%ss:xtemp2 /* we can do this: SS is Kernel Data Segment */
        
        /* ############################################# */     
        /* NOTE: ESP points to CS (pushed after EIP :-)  */
        /* ############################################# */     

        /* %edi points now to space where EIP,CS and EFLAGS have to be moved to */
        movl    0(%esp),%eax
        movl    %eax,%ss:0(%edi)        /* transfer EIP */
        movl    4(%esp),%eax
        movl    %eax,%ss:4(%edi)        /* transfer CS */
        movl    8(%esp),%eax
//        and     $0xFFFFBFFF,%eax        /* turn off NT Bit */
        movl    %eax,%ss:8(%edi)        /* transfer EFLAGS */
        
//	    movl    $0x38,%eax
//		ltr     %ax
//		hlt
        /* restore the previous saved %eax from the kernel data segment */
        movl    %ss:xtemp2,%eax
    
        /* remember: SS already set */
        movl    %edi,%esp       /* now forget the old stack, move to the new */
        movl    %ss:xtemp,%edi  /* restore the orignal %edi */

        /* If no PRL-change takes place, ia32 does not change the ESP and SS
         * on return. So, the CS:EIP and EFLAGS had to be transferred before.
         * In current Topsy 2.0, only one SS is present for the Kernel. So,
         * We can rely on that.
         */
        /* and now: go to heaven or hell */
        
    __PRLChange:
/*
pushl %esp
call MyDumpStack
hlt
*/ /* 
pushl %esp
pushl %cs
pushl %es
pushl %ds
pushl %ss
pushl %gs
pushl %fs
str   %ax
pushl %eax
sldt  %ax
pushl %eax
call DumpImpRegs
*/
        GOTO_HEAVEN_OR_HELL
        
//-LR         iret                    /* and go either to heaven or hell !! */

/**End of restoreContext()*****************************************************/

    ASM_PROC_FRAME(automaticThreadExit)
        /* This code performs transparently to the user a tmThreadExit() when
         * the main thread routine returns 
         *           -------------- : top of stack == stackStart
         * sizeof(     exitMessage
         *  exitMsg) ..............
         * exitCode    exitCode
         *   length  .............. :
         * sizeof(     Argument 1   : This part is a machine dependent setup, I think!
         *   void*)  .............. : Nonethelss, we keep it due to the lack of time for
         * sizeof(     Argument 0   : adjusting all the existing code where needed.
         *   void*)  .............. :
         * sizeof(     RETURNADDR   : Address of exitCode
         *      sp   -------------- : stack pointer when thread starts
         */
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
    nop
        movl    $endAutomaticThreadExit,%eax    /* Calculate size of    */
        movl    $automaticThreadExit,%ebx       /* automaticThreadExit()*/ 
        subl    %ebx,%eax                       /* calculate it by subtraction */       
        /* the stack pointer points to the beginning of automaticThreadExit */
        addl    %esp,%eax                       /* add the stack pointer to it, as relative to stack=data=code segment */
        addl    $8,%eax                         /* skip both arguments on stack laying before the automatic ThreadExit */
        movl    %eax,%esi               /* MESSAGE ADDRESS */   
        movl    $dSYSCALL_SEND_OP,%eax  /* SYSCALL TO BE PERFORMED */
        movl    $0,%ebx                 

        SYSCALL                         /* and launch it !!!! */
    ASM_PROC_FRAME(endAutomaticThreadExit)


    /* Either syscallExceptionHandler() and hwExceptionHandler() are both
       implemented using the procedure _INTHandler(). This function disposes
       control to the appropriate C-function directly. I chose this implementation
       to try developping a cleaerer process flow from user to kernel mode and vice
       versa. 
     */
   
    ASM_PROC_FRAME(syscallExceptionHandler)
    ASM_PROC_FRAME(hwExceptionHandler)


/*Lowest Level Interrupt Handling*********************************************/    
    
/* Define the Exception Handlers
 */
.align 4
IDTVEC(__NEx_00)                /* Divide Error */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_00)           /* call trap handler */
.align 4
IDTVEC(__NEx_01)                /* Debug Exception */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_01)           /* call trap handler */
.align 4
IDTVEC(__NEx_02)                /* Nonmaskable Interrupt */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_02)           /* call trap handler */
.align 4
IDTVEC(__NEx_03)                /* Breakpoint (one byte INT 3 Instruction) */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_03)           /* call trap handler */
.align 4
IDTVEC(__NEx_04)                /* Overflow (INTO) */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_04)           /* call trap handler */
.align 4
IDTVEC(__NEx_05)                /* Bounds check (BOUND) */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_05)           /* call trap handler */
.align 4
IDTVEC(__NEx_06)                /* Invalid Opcode */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_06)           /* call trap handler */
.align 4
IDTVEC(__NEx_07)                /* Coprocessor not available */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_07)           /* call trap handler */
.align 4
IDTVEC(__NEx_08)                /* Double fault (Error Code) */
        TRAP(T_EX_08)           /* call trap handler */
.align 4
IDTVEC(__NEx_09)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_09)           /* call trap handler */
.align 4
IDTVEC(__NEx_0A)                /* Invalid TSS (Error Code) */
        TRAP(T_EX_0A)           /* call trap handler */
.align 4
IDTVEC(__NEx_0B)                /* Segment not present (Error Code) */
        TRAP(T_EX_0B)           /* call trap handler */
.align 4
IDTVEC(__NEx_0C)                /* Stack exception (Error Code) */
        TRAP(T_EX_0C)           /* call trap handler */
.align 4
IDTVEC(__NEx_0D)                /* General Protection (Error Code) */
        TRAP(T_EX_0D)           /* call trap handler */
.align 4
IDTVEC(__NEx_0E)                /* Page Fault (Error Code) */
        TRAP(T_EX_0E)           /* call trap handler */
.align 4
IDTVEC(__NEx_0F)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_0F)           /* call trap handler */
.align 4
IDTVEC(__NEx_10)                /* Coprocessor error */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_10)           /* call trap handler */
.align 4
IDTVEC(__NEx_11)                /* Alignment check (Error Code) */
        TRAP(T_EX_11)           /* call trap handler */
.align 4
IDTVEC(__NEx_12)                /* Machine check (Error Code) */
        TRAP(T_EX_12)           /* call trap handler */
 
.align 4
IDTVEC(__NEx_13)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_13)           /* call trap handler */
.align 4
IDTVEC(__NEx_14)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_14)           /* call trap handler */
.align 4
IDTVEC(__NEx_15)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_15)           /* call trap handler */
.align 4
IDTVEC(__NEx_16)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_16)           /* call trap handler */
.align 4
IDTVEC(__NEx_17)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_17)           /* call trap handler */
.align 4
IDTVEC(__NEx_18)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_18)           /* call trap handler */
.align 4
IDTVEC(__NEx_19)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_19)           /* call trap handler */
.align 4
IDTVEC(__NEx_1A)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_1A)           /* call trap handler */
.align 4
IDTVEC(__NEx_1B)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_1B)           /* call trap handler */
.align 4
IDTVEC(__NEx_1C)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_1C)           /* call trap handler */
.align 4
IDTVEC(__NEx_1D)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_1D)           /* call trap handler */
.align 4
IDTVEC(__NEx_1E)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_1E)           /* call trap handler */
.align 4
IDTVEC(__NEx_1F)                /* reserved */
        pushl   $0              /* dummy error Code */
        TRAP(T_EX_1F)           /* call trap handler */

/* Define the Interrupt Handlers.
 */
IRQ(__NIR_00,T_IR_00)
IRQ(__NIR_01,T_IR_01)
IRQ(__NIR_02,T_IR_02)
IRQ(__NIR_03,T_IR_03)
IRQ(__NIR_04,T_IR_04)
IRQ(__NIR_05,T_IR_05)
IRQ(__NIR_06,T_IR_06)
IRQ(__NIR_07,T_IR_07)
IRQ(__NIR_08,T_IR_08)
IRQ(__NIR_09,T_IR_09)
IRQ(__NIR_0A,T_IR_0A)
IRQ(__NIR_0B,T_IR_0B)
IRQ(__NIR_0C,T_IR_0C)
IRQ(__NIR_0D,T_IR_0D)
IRQ(__NIR_0E,T_IR_0E)

#ifdef APIC_SPIV_HANDLING_IRET
#  if APIC_IR_SPIV != T_IR_0F
#    error "adjust code here!"
#  endif
/* Spurious Interrupt Vector handling with an direct iret */
IDTVEC(__NIR_0F)
  iret
#else
IRQ(__NIR_0F,T_IR_0F)
#endif


IRQ(__INT_30,T_IS_30)   /* Software Interrupt !! equals to "SYSCALL" :-) */
IRQ(__INT_31,T_IS_31)   /* Software Interrupt: Block all other */ 

    ASM_PROC_FRAME(__general_ExceptionHandler)
        movl    %cr3, %eax // save pdbr
        pushl   %eax 
        pushl   %ds
        pushl   %es
        pushl   %fs
        pushl   %gs
        
#if 0   // this is done in _INTHandler 
        /* Find Thread struct of running Task */
        /*  the Thread struct is at the bottom of the currently used */
        /*  stack which is 8k in size and alligned to the 8k boundary*/
        call    __get_thread_from_esp                     /* get Thread structure                                 */
        movl    %eax, %esi                                /* Save the address of the Thread structure in esi      */
        movl    TH_CTX(%esi), %edi                        /* Save the address of the ProcContext structure in edi */

        /* save some additional values in the Stack Frame                                                         */ 
        pushl   TH_STAT(%esi)                             /* the original status                                  */
        pushl   TH_PRIO(%esi)                             /* the original priority                                */
        pushl   CTX_SF(%edi)                              /* the last pointer is the original Stack Frame         */

        /* save the new address of the Stack Frame in the ProcContext                                             */
        movl    %esp, CTX_SF(%edi)                        /* the Stack Pointer points to the Stack Frame          */


        /* the Stack Frame is complete, therefore we can allow interrupts */
        sti
#endif

        /* make room for 3 other fields of the stack frame: last pointer, priority and schedFlags */
        subl    $12, %esp   

        movl    $GC_KDSEL,%eax
        movw    %ax,%ds
        movw    %ax,%es
        
        call _INTHandler        /* I suppose _INTHandler() always calls */
                                /* restoreContext() :-) */
        cli                     /* If this _INTHandler() ever should */
        hlt                     /* return, hang the machine: I'm human.  */
           

    ASM_PROC_FRAME(__get_thread_from_esp)
        movl    %esp, %eax
        andl    $~(TM_KERNEL_MODE_STACK_SIZE-1), %eax
        ret

/*=============================================================================
   void _endOfIRQ(uchar pIRQNo);
   Sends End Of Interrupt to PICU (or Both) based on IRQ number (0-15)
   If IRQnum is 0-7 then we send to 1, else we send to 1 then 2.
 */
ASM_PROC_FRAME(__endOf_IRQ)
        ENTER   
        pushl   %eax            
        movl    8(%ebp),%eax    /*get irq number (0-15) */
        movb    %al,%ah
        movb    $0x20,%al
        outb    %al,$PIC1_IO    /* send EOI to PIC 1 */
        cmpb    $7,%ah          /* pic1 only? */
        jbe     eoi00           /* yes */
        outb    %al,$PIC2_IO    /* send to PIC 2 also */
eoi00:
        popl    %eax    
        LEAVE

/*===============================================
   void _disableIRQ(uchar pIRQNo)  masks the IRQ number specified (0-15).
   The proper PICU is selected based on the IRQ number.
 */
ASM_PROC_FRAME(__disable_IRQ)
        ENTER
        pushfl
        cli
        pushl   %eax
        pushl   %ecx
        pushl   %edx
        movl    $1,%eax
        movl    8(%ebp),%ecx            /* get irq number (0-15) */
        andl    $0x0F,%ecx              /* (0-15) */
        shll    %cl,%eax                /* set the bit for the irq (0-7) or (8-15) */
        andb    %al,%al
        jz      dirq2
        movl    $PIC1_MASK,%edx
        jmp     dirq3
dirq2:
        movl    $PIC2_MASK,%edx
        movb    %ah,%al
dirq3:
        movb    %al,%ah
        inb     %dx,%al
        pushl   %eax
        popl    %eax
        orb     %ah,%al
        outb    %al,%dx
        
        popl    %edx
        popl    %ecx
        popl    %eax
        popfl                           /* give em their flags back */
        
        LEAVE

/*===============================================
   void _enableIRQ(uchar pIRQNo)  UNmasks the IRQ number specified (0-15).
   The proper PICU is selected based on the IRQ number.
 */
ASM_PROC_FRAME(__enable_IRQ)
        ENTER
        pushfl
        cli                     /* previous state is reset on popfl */
        pushl   %eax            
        pushl   %ecx
        pushl   %edx
        
        movl    $1,%eax
        movl    8(%ebp),%ecx    /* get irq number (0-15) */
        andl    $0x0F,%ecx      /* (0-15 only) */
        shll    %cl,%eax        /* set the bit for the irq (0-7) */
        andb    %al,%al
        jz      eirq2
        movl    $PIC1_MASK,%edx
        jmp     eirq3
eirq2:
        movl    $PIC2_MASK,%edx
        movb    %ah,%al
eirq3:  
        movb    %al,%ah
        inb     %dx,%al
        pushl   %eax
        popl    %eax
        notb    %ah
        andb    %ah,%al
        outb    %al,%dx
        
        popl    %edx
        popl    %ecx
        popl    %eax
        popfl
        
        LEAVE

    
/* Preserve static temporary space in the kernel data segment for modifying the
   thread stack if a kernel thread has to be started  */
    
.data    
.align 4,0x9090

xtemp:
        .long   0x0

xtemp2:
        .long   0x0
