/*
    mmHalAsm.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/Memory/ia32/mmHalAsm.S,v $
    Author(s):             Lukas Ruf, lr@lpr.ch
    Affiliation:           ETH Zuerich, TIK
    Version:               $Revision: 3037 $
    Creation Date:         
    Last Date of Change:   $Date: 2007-06-03 23:23:51 +0200 (Son, 03 Jun 2007) $      by: $Author: sryffel $
*/
.section .sasm
.align  4,0x9090

#include "asm.h"                /* Topsy/i386/ */
#include "MemoryLayout.h"       /* Memory/i386/ */

#define KILOBYTE        1024
#define MEGABYTE        1024*KILOBYTE
#define HALFMEGA         512*KILOBYTE
#define BITSPERPAGE     4096*8

////
// void _emptyPUT(ushort MemSelector);
////
ASM_PROC_FRAME(__emptyPUT)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edi
        pushw   %es
        
        movl    8(%ebp),%eax
        movw    %ax,%es
        xorl    %eax,%eax
        xorl    %edi,%edi
        movl    $4095,%ecx
        rep
        stosb

        popw    %es
        popl    %edi
        popl    %ecx
        popl    %eax
        LEAVE
////
// ulong _maxPhysMem();
///
ASM_PROC_FRAME(__maxPhysMem)
        ENTER
        pushl   %ebx
        pushl   %ecx
        pushw   %ds

        movl    $GC_OS_GLOBAL_SEL,%eax
        movw    %ax,%ds
        movl    $HALFMEGA,%eax
        movl    $0x12345678,%ebx
_mPML1:
        movl    (%eax),%ecx     // store original value
        movl    %ebx,(%eax)     // write test value
        cmpl    %ebx,(%eax)     // compare
        movl    %ecx,(%eax)     // restore original value
        jne     _mPML2
        addl    $HALFMEGA,%eax
        jmp     _mPML1
_mPML2:
        
        popw    %ds
        popl    %ecx
        popl    %ebx
        LEAVE

////
// ulong _memPagesAvail(ushort pPUTSelector);
// return number of free pages in PUT
///
ASM_PROC_FRAME(__memPagesAvail)
        ENTER
        pushl   %ebx
        pushl   %esi
        pushw   %ds

        movl    8(%ebp),%eax
        movw    %ax,%ds
        
        xorl    %eax,%eax
        xorl    %ebx,%ebx
        xorl    %esi,%esi
        
_mPAL1:
        bt      %ebx,(%esi)
        jc      _mPAL2
        incl    %eax
_mPAL2:
        incl    %ebx
        cmpl    $BITSPERPAGE,%ebx
        jl      _mPAL1
        
        popw    %ds
        popl    %esi
        popl    %ebx
        LEAVE

////
// ulong _memPagesUsed(ushort pPUTSelector);
// return number of occupied pages in PUT
///
ASM_PROC_FRAME(__memPagesUsed)
        ENTER
        pushl   %ebx
        pushl   %esi
        pushw   %ds

        movl    8(%ebp),%eax
        movw    %ax,%ds
        
        xorl    %eax,%eax
        xorl    %ebx,%ebx
        xorl    %esi,%esi
        
_mMAL1:
        bt      %ebx,(%esi)
        jnc     _mMAL2
        incl    %eax
_mMAL2:
        incl    %ebx
        cmpl    $BITSPERPAGE,%ebx
        jl      _mMAL1
        
        popw    %ds
        popl    %esi
        popl    %ebx
        LEAVE

////
// ulong _getFreePage(ushort pPageOrganizerSelector);
////
ASM_PROC_FRAME(__getFreePage)
        ENTER
        pushl   %esi
        pushw   %ds
        movl    8(%ebp),%eax
        movw    %ax,%ds
        xorl    %eax,%eax
        xorl    %esi,%esi
_gFPL1: 
        bt      %eax,(%esi)
        jnc     _gFPL2
        incl    %eax
        jmp     _gFPL1
_gFPL2:
        
        popw    %ds
        popl    %esi
        LEAVE

////                            
// uchar _setPageUsed(ushort pPageOrganizerSelector, ulong pPage);
// report previous state
////
ASM_PROC_FRAME(__setPageUsed)
        ENTER
        pushl   %esi
        pushw   %ds
        movl    8(%ebp),%eax
        movw    %ax,%ds
        movl    12(%ebp),%eax
        movl    $0,%esi
        bts     %eax,(%esi)
        jc      _sPU1
        xorl    %eax,%eax
        jmp     _sPU2
_sPU1:
        movl    $1,%eax
_sPU2:  
        popw    %ds     
        popl    %esi
        LEAVE

////                            
// uchar _setPageUnUsed(ushort pPageOrganizerSelector, ulong pPage);
// report previous state
////
ASM_PROC_FRAME(__setPageUnUsed)
        ENTER
        pushl   %eax
        pushl   %esi
        pushw   %ds
        movl    8(%ebp),%eax
        movw    %ax,%ds
        movl    12(%ebp),%eax
        movl    $0,%esi
        btr     %eax,(%esi)
        jc      _sPUU1
        xorl    %eax,%eax
        jmp     _sPUU2
_sPUU1:
        movl    $1,%eax
_sPUU2: 
        
        popw    %ds     
        popl    %esi
        popl    %eax
        LEAVE

/*GDT*************************************************************************/ 

////
// _set_GDTEntry(ushort uSelector, ulong uBASE, ulong uLIMIT, byte uACCESS_B5, byte uTYPE_B6);
//      pushl   %edx    : %dl = uTYPE_B6                        (+24)
//      pushl   %ecx    : %cl = uACCESS_B5                      (+20)
//      pushl   %ebx    : uLIMIT (REMEMBER: Granularity)        (+16)
//      pushl   %eax    : uBASE                                 (+12)
//      pushl   %eax    : uSelector                             (+08)
//      call    _set_GDTEntry
ASM_PROC_FRAME(__set_GDTEntry)
        ENTER
        
        pushl   %eax
        pushl   %ebx
        pushl   %ecx
        pushl   %edx
        pushl   %edi

        pushw   %es
        
        movl    $GC_GDT_SEL,%eax
        movw    %ax,%es

        movl    8(%ebp),%ebx
        movl    %ebx,%edi               //es:[edi] = Byte0(Descriptor)
        
        movl    12(%ebp),%eax           // BASE
        movl    16(%ebp),%ebx           // LIMIT
        movl    20(%ebp),%ecx           // ACCESS Byte 5
        movl    24(%ebp),%edx           // TYPE Byte 6
        
		// Decrement by 1 the value of LIMIT because in the GDTE:
		// 0 means 1 page (or 1 byte)
		// 1 means 2 pages (or 2 bytes)
		// ..
		// 2^20-1 means 2^20 pages (or 2^20 bytes)
		// -> max segment size is 2^20 * 4096 = 4GB.
		decl	%ebx
		
        // now: fill descriptor
        movb    %bl,%es:(%edi)
        incl    %edi
        movb    %bh,%es:(%edi)
        incl    %edi                    // Limit 00..15 set
        shrl    $16,%ebx
        
        movb    %al,%es:(%edi)
        incl    %edi
        movb    %ah,%es:(%edi)
        incl    %edi                    // Base  00..15 set
        shrl    $16,%eax
                
        movb    %al,%es:(%edi)
        incl    %edi                    // Base  16..23 set
        movb    %cl,%es:(%edi)
        incl    %edi                    // Access Byte 5 set
                
        andb    $0xf0,%dl               // Blank out lower nibble of uTYPE_B6
        andb    $0x0f,%bl               // Blank out upper nibble of uLIMIT
        orb     %dl,%bl                 // Create Byte 6 !!!
        movb    %bl,%es:(%edi)
        incl    %edi                    // Type Byte 6 set
        
        movb    %ah,%es:(%edi)
        incl    %edi                    // Base  24..31 set
        
        call    __reset_TLB
        
        popw    %es
        
        popl    %edi
        popl    %edx
        popl    %ecx
        popl    %ebx
        popl    %eax
        LEAVE
        
////
// void _reset_TLB();
////
ASM_PROC_FRAME(__reset_TLB)
        ENTER
        pushfl  
        pushl   %eax
        cli
        
        movl    %cr3,%eax       // perform a reset by reloading the
        movl    %eax,%cr3       // page directory table
                
        popl    %eax
        popfl   
        LEAVE

////    
// _set_GDTE(ushort pEntry, t_GDTE *pGDTE)      
//      pushl   %eax    (%eax = pointer to GDTE)
//      pushl   %eax    (%ax  = entry number of GDTE)
//      call    _set_GDTE
////
ASM_PROC_FRAME(__set_GDTE)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edi
        pushl   %esi
        pushw   %es

        movl    $GC_GDT_SEL,%eax
        movw    %ax,%es
        movl     8(%ebp),%eax           // get entry number
        movl    %eax,%edi
        
        movl    12(%ebp),%esi
        movl    $8,%ecx

        rep
        movsb
        
        call    __reset_TLB
        
        popw    %es
        popl    %esi
        popl    %edi
        popl    %ecx
        popl    %eax
        LEAVE
        
////    
// _get_GDTE(ushort pEntry, t_GDTE *pGDTE)      
//      pushl   %eax    (%eax = pointer to GDTE)
//      pushl   %eax    (%ax  = entry number of GDTE)
//      call    _set_GDTE
////
ASM_PROC_FRAME(__get_GDTE)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edi
        pushl   %esi
        pushw   %ds

        movl    $GC_GDT_SEL,%eax
        movw    %ax,%ds
        movl     8(%ebp),%esi           // get entry number
        
        movl    12(%ebp),%edi
        movl    $8,%ecx

        rep
        movsb
        
        popw    %ds
        popl    %esi
        popl    %edi
        popl    %ecx
        popl    %eax
        LEAVE   
        
////
// void _set_GDTR(ushort plimit, ulong pbase);
////
ASM_PROC_FRAME(__set_GDTR)
        ENTER
        subl    $16,%esp
        pushfl
        pushl   %eax
        pushl   %ebx
        pushl   %edi
        movl    8(%ebp),%ebx    // plimit
        movl    12(%ebp),%eax   // pbase
        leal    -8(%ebp),%edi   // temp. storage of t_GDTR lGDTR;
        movw    %bx,(%edi)      // lGDTR.limit  = plimit;
        movl    %eax,2(%edi)    // lGDTR.base   = pbase;
        cli
        lgdt    (%edi)
        popl    %edi
        popl    %ebx
        popl    %eax
        popfl
        LEAVE

////
// void _get_GDTR(ushort *plimit, ulong *pbase);
////
ASM_PROC_FRAME(__get_GDTR)
        ENTER
        subl    $16,%esp
        
        pushl   %eax
        pushl   %ebx
        pushl   %ecx
        pushl   %edi

        leal    -8(%ebp),%edi   // temp. storage of t_GDTR lGDTR;
        sgdt    (%edi)          // write GDTR
        
        movl    8(%ebp),%ebx    // plimit
        movl    12(%ebp),%eax   // pbase
        xorl    %ecx,%ecx       // clear high 16b of %ecx
        movw    (%edi),%cx
        movw    %cx,(%ebx)      // *plimit = lGDTR.limit;
        movl    2(%edi),%ecx
        movl    %ecx,(%eax)     // *pbase  = lGDTR.base;
        
        popl    %edi
        popl    %ecx
        popl    %ebx
        popl    %eax
        
        LEAVE

/*IDT*************************************************************************/ 

////
// _set_IDTEntry(ushort pIDTSel, ushort uINTNo, ushort pCodeSel, ulong uOFFS, ushort INTType);
//      pushl uINTType  (al = INTType) {Interrupt, Trap or Task Gate}
//      pushl uOFFS
//      pushl pCodeSel
//      pushl uINTNo (al == INTNo)
//      pushl pIDTSel   : Selector of active IDT
//      call _setIDTEntry
//
// !! PRECONDITION: Valid Selector for IDT_Segment is existing !!
////
ASM_PROC_FRAME(__set_IDTEntry)
        ENTER
        
        pushl   %eax
        pushl   %ebx
        pushl   %edx

        pushl   %edi
        
        pushw   %es
        
        movl    8(%ebp),%eax    // pIDTSel      
        movw    %ax,%es 

        movl    12(%ebp),%eax   // INT No. in %ax (OFFSET to ISR Reference)
        movl    %eax,%edi       // es:[edi] -> ITDE[0]
        
        movl    16(%ebp),%eax   // Code Selector
        movl    20(%ebp),%ebx   // Offset
        movl    24(%ebp),%edx   // INT Type  (%dx)
        
        movw    %bx,%es:(%edi)  // Offset 00:15
        movw    %ax,%es:2(%edi) // Code Selector
        movw    %dx,%es:4(%edi) // INT Type (%dx)
        shrl    $16,%ebx
        movw    %bx,%es:6(%edi) // Offset 16:31

        popw    %es

        popl    %edi
        
        popl    %edx
        popl    %ebx
        popl    %eax
        
        LEAVE
        
////    
// void _get_IDTE(ushort pIDTSel, ushort pEntry, t_IDTE *pIDTE);        
////
ASM_PROC_FRAME(_get_IDTE)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edi
        pushl   %esi
        pushw   %ds

        movl    8(%ebp),%eax
        movw    %ax,%ds
        movl    12(%ebp),%esi           // get entry number
        
        movl    16(%ebp),%edi
        movl    $8,%ecx

        rep
        movsb
        
        popw    %ds
        popl    %esi
        popl    %edi
        popl    %ecx
        popl    %eax
        LEAVE
        
////
// void _reset_IDTR();
////
ASM_PROC_FRAME(__reset_IDTR)
        ENTER
        subl    $8,%esp
        pushfl
        cli
        pushl   %eax
        leal    -8(%ebp),%eax
        sidt    (%eax)
        lidt    (%eax)
        popl    %eax
        popfl   
        LEAVE

////
// void _set_IDTR(ushort plimit, ulong pbase);
////
ASM_PROC_FRAME(__set_IDTR)
        ENTER
        subl    $8,%esp
        pushfl
        pushl   %eax
        pushl   %ebx
        pushl   %edi
        movl    8(%ebp),%ebx    // plimit
        movl    12(%ebp),%eax   // pbase
        leal    -8(%ebp),%edi   // temp. storage of t_IDTR lIDTR;
        movw    %bx,(%edi)      // lIDTR.limit  = plimit;
        movl    %eax,2(%edi)    // lIDTR.base   = pbase;
        cli
        lidt    (%edi)
        popl    %edi
        popl    %ebx
        popl    %eax
        popfl
        LEAVE

////
// void _get_IDTR(ushort *plimit, ulong *pbase);
////
ASM_PROC_FRAME(__get_IDTR)
        ENTER
        subl    $8,%esp
        
        pushl   %eax
        pushl   %ebx
        pushl   %ecx
        pushl   %edi
        
        leal    -8(%ebp),%edi   // temp. storage of t_IDTR lIDTR;
        sidt    (%edi)          // write IDTR
        movl    8(%ebp),%ebx    // plimit
        movl    12(%ebp),%eax   // pbase
        xorl    %ecx,%ecx       // clear high 16b of %ecx
        movw    (%edi),%cx
        movw    %cx,(%ebx)      // *plimit = lIDTR.limit;
        movl    2(%edi),%ecx
        movl    %ecx,(%eax)     // *pbase  = lIDTR.base;
        
        popl    %edi
        popl    %ecx
        popl    %ebx
        popl    %eax
        
        LEAVE


/*TSS*************************************************************************/ 

////
// void _createTSS(unsigned short pAliasSel);
////
ASM_PROC_FRAME(__createTSS)
        ENTER
        pushl   %eax
        pushl   %ecx
        pushl   %edi
        pushl   %esi
        push    %es
        push    %ds
        
        movl    ARG1,%eax               // get Selector Alias
        movw    %ax,%es

        push    %es                     // set data to Alias for easy modification
        pop     %ds
        
        xorl    %edi,%edi
        
        pushl   %edi
        
        movl    $25,%ecx                // 104 / 4 = 26 - 1
        xorl    %eax,%eax
        rep     
        stosl                           // Clear out TSS

        popl    %edi
        
        // Create TSS in TSS-Alias
        movw    %ss,_SS0(%edi)          // SS of Kernel
        movw    %ss,_SS1(%edi)          // SS of Driver
        movw    %ss,_SS2(%edi)          // SS of Software Driver
        movl    $BOOTSTACKTOP,%eax
        movl    %eax,_ESP0(%edi)        // Exception Stack Top
        movl    %eax,_ESP1(%edi)        // Exception Stack Top
        movl    %eax,_ESP2(%edi)        // Exception Stack Top
        movw    %es,_ES(%edi)
        movw    %cs,_CS(%edi)
        movw    %ss,_SS(%edi)
        movw    %ds,_DS(%edi)
        movw    %fs,_FS(%edi)
        movw    %gs,_GS(%edi)
        movw    $0xFFFF,_IOBASE(%edi)   // No IO Permission Bit Map is required
//-CJ        movw    $0x0,_IOBASE(%edi)   // No IO Permission Bit Map is required
//-CJ        movw    $0x1,_DEBUGbit(%edi) //-CJ set debug bit to see if something happens


        pop     %ds
        pop     %es
        popl    %esi
        popl    %edi
        popl    %ecx
        popl    %eax
        LEAVE

/* __activateTSS(unsigned short pTSS_Selector); as the name tells you:-)
 */     
ASM_PROC_FRAME(__activateTSS)    
        ENTER
        pushl   %eax
        movl    ARG1,%eax               // Get TSS Selector
        ltr     %ax                     // Load Task State Register
        popl    %eax
        LEAVE
