/*
    SupportAsm.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/Topsy/ia32/SupportAsm.S,v $
 	Author(s):             Lukas Ruf, lr@lpr.ch
 	Affiliation:           ETH Zuerich, TIK
 	Version:               $Revision: 2922 $
 	Creation Date:         
 	Last Date of Change:   $Date: 2007-03-19 17:48:52 +0100 (Mon, 19 Mär 2007) $      by: $Author: baderma $
*/

#include "asm.h"
#include "cpu.h"

        .section .sasm
        .align 4,0x9090

/* Boolean testAndSet(Boolean *LockVar);
 * Multiprocessor able version of Topsy-testAndSet(). No Interrupt Disable is
 * required, as only the location is modified but not copied into a register.   
 */ 
ASM_PROC_FRAME(testAndSet)
        ENTER
        pushfl
        pushl   %ebx
        
        movl    $1,%eax
        movl    ARG1,%ebx       /* point at mutex */
        
        //-LR Version of testAndSet() as seen by Mach4:
        //    mach4-i386/libthreads/lock.S
        xchg    %eax,(%ebx)     /* swap with mutex: xchg with memory is 
                                   automatically locked */
        xorl    $1,%eax         /* 1 (locked)   => FALSE */
                                /* 0 (unlocked) => TRUE  */
//-LR         bts     %eax,(%ebx)     /* dereference memory location directly */
//-LR         jc      _isalreadyset
//-LR         movl    $0x01,%eax      /* return true: set was successfull ! */
//-LR         jmp     _endtestandset
//-LR _isalreadyset:
//-LR         xorl    %eax,%eax       /* return false: LockVar was already set */
//-LR _endtestandset:
        popl    %ebx
        popfl
        LEAVE

ASM_PROC_FRAME(testAndUnSet)
        ENTER
        pushfl
        pushl   %ebx
        
        movl    $0,%eax
        movl    ARG1,%ebx       /* point at mutex */
        
        //-LR Version of testAndSet() as seen by Mach4:
        xchg    %eax,(%ebx)     /* swap with mutex: xchg with memory is 
                                   automatically locked */
//-LR         bts     %eax,(%ebx)     /* dereference memory location directly */
//-LR         jc      _isalreadyset
//-LR         movl    $0x01,%eax      /* return true: set was successfull ! */
//-LR         jmp     _endtestandset
//-LR _isalreadyset:
//-LR         xorl    %eax,%eax       /* return false: LockVar was already set */
//-LR _endtestandset:
        popl    %ebx
        popfl
        LEAVE

////
// void __mutex(ulong *p32mutex, ulong pmutex); blocking mutual exclusion
// __mutex() is completely multiprocessing able. The bus is implicitely locked
// by the i386-instruction btr/bts. There is no need of disable interrupts 
// or block the whole system.
// __mutex() and _demutex() set or reset a single bit in p32mutex (=array of 32 bits).
// __mutex(): if a bit is already set, block the process without return ad aeternam
// as long as there is no setting of the bit.   

ASM_PROC_FRAME(__mutex)
        ENTER
        pushfl
        pushl   %eax
        pushl   %ebx
        movl    ARG1,%ebx
        movl    ARG2,%eax
        andl    $0x1F,%eax      // => eax % 32 // make sure index points to 32first bits only
_mutexL1:
        bts     %eax,(%ebx)
        jc      _mutexL1        // block if already set !
        popl    %ebx
        popl    %eax
        popfl
        LEAVE

ASM_PROC_FRAME(__demutex)
        ENTER
        pushfl
        pushl   %eax
        pushl   %ebx

        movl    ARG1,%ebx
        movl    ARG2,%eax
        andl    $0x1F,%eax              // => eax % 32 // make sure index points to 32first bits only
        
        btr     %eax,(%ebx)             // reset bit p32mutex[pmutex]
        
        popl    %ebx
        popl    %eax
        popfl
        LEAVE


//Topsy i386                                            Lukas Ruf, December 1997
//------------------------------------------------------------------------------
////
// Simple Program to demonstrate working PM_Switch
//
////

      speaker_port = 0x61
       pit_control = 0x43
     pit_channel_2 = 0x42
          pit_freq = 0x1234dd
          lofreq   = 0x0A9               // (PIT_FREQ / 1000) AND 0FFh
          hifreq   = 0x004               // (PIT_FREQ / 1000) AND 0FF00h

.extern __wait


////
// void beep()
////
ASM_PROC_FRAME(__beep)
        pushl   %eax
        pushl   %ecx
        pushl   %edx

        movl    $pit_control,%edx
        movb    $0xb6,%al
        outb    %al,%dx
        movl    $pit_channel_2,%edx
        movb    $lofreq,%al
        outb    %al,%dx
        movb    $hifreq,%al
        outb    %al,%dx

        movl    $speaker_port,%edx        //; on
        inb     %dx,%al
        orb     $3,%al
        outb    %al,%dx

        call    __wait

        movl    $speaker_port,%edx
        inb     %dx,%al
        andb    $0xfc,%al
        outb    %al,%dx

        popl    %edx
        popl    %ecx
        popl    %eax    
        ret

ASM_PROC_FRAME(__reboot)
    
        jmp     _r1
_rspac1:
        .byte   0,0
_rspac2:
        .long   0
_r1:    
        cli
        leal    _rspac2,%eax
        movl    $0000,(%eax)            // real mode idt base
        leal    _rspac1,%eax
        movw    $1023,(%eax)            // real mode idt limit (256x4B)
        lidt    (%eax)
        
        movl    %cr0,%eax
        movl    $0x80000001,%ebx        // switch back to RM, disable paging
        notl    %ebx
        andl    %ebx,%eax
        movl    %eax,%cr0
        xorl    %eax,%eax
        movl    %eax,%cr3               // reset Page Directory Base Addr.
        
        jmp     _rflush
        nop     
        nop
_rflush:        
        movl    $0x1234,%eax            // prepare for Warm Reset
        movl    $0x40,%ebx              // do not test RAM
        movw    %bx,%es
        movl    $0x72,%edi
        stosw   
        ljmp    $0xF000,$0xFFF0

////
// void  __outb(unsigned char pbyte, unsigned short pport)
////
ASM_PROC_FRAME(__outb)
        ENTER
        pushl   %eax
        pushl   %edx

        movl    8(%ebp),%eax
        movl    12(%ebp),%edx
        outb    %al,%dx

        popl    %edx
        popl    %eax
        LEAVE

////
// void  __outw(unsigned short pword, unsigned short pport)
////
ASM_PROC_FRAME(__outw)
        ENTER
        pushl   %eax
        pushl   %edx

        movl    8(%ebp),%eax
        movl    12(%ebp),%edx
        outw    %ax,%dx

        popl    %edx
        popl    %eax
        LEAVE

////
// void  __outl(unsigned int pdword, unsigned short pport)
////	
ASM_PROC_FRAME(__outl)
        ENTER
        pushl   %eax
        pushl   %edx

        movl    8(%ebp),%eax
        movl    12(%ebp),%edx
        outl    %eax,%dx

        popl    %edx
        popl    %eax
        LEAVE

////
// unsigned char __inb(unsigned short pport)
////
ASM_PROC_FRAME(__inb)
        ENTER
        pushl   %edx

        xorl    %eax,%eax
        movl    8(%ebp),%edx
        inb     %dx,%al

        popl    %edx
        LEAVE

////      
// unsigned short __inw(unsigned short pport)
////	
ASM_PROC_FRAME(__inw)
        ENTER
        pushl   %edx

        xorl    %eax,%eax
        movl    8(%ebp),%edx
        inw     %dx,%ax

        popl    %edx
        LEAVE

////      
// unsigned int __inl(unsigned short pport)
////
ASM_PROC_FRAME(__inl)
        ENTER
        pushl   %edx

        xorl    %eax,%eax
        movl    8(%ebp),%edx
        inl     %dx,%eax

        popl    %edx
        LEAVE
        
////
// unsigned char __readb(unsigned short selector, unsigned int offset)
////
ASM_PROC_FRAME(__readb)
	ENTER

	pushfl
	pushl %es
	pushl %esi

	movw ARG1,%ax // set the selector
	movw %ax,%es

	movl ARG2,%eax
	movl %eax,%esi // init source index with offset
	movb %es:0(,%esi,1),%al
	
	popl %esi
	popl %es
	popfl

	LEAVE

////
// unsigned short __readw(unsigned short selector, unsigned int offset)
////
ASM_PROC_FRAME(__readw)
	ENTER

	pushfl
	pushl %es
	pushl %esi

	movw ARG1,%ax // set the selector
	movw %ax,%es

	movl ARG2,%eax
	movl %eax,%esi // init source index with offset
	movw %es:0(,%esi,1),%ax
	
	popl %esi
	popl %es
	popfl

	LEAVE

////
// unsigned int __readl(unsigned short selector, unsigned int offset)
////
ASM_PROC_FRAME(__readl)
	ENTER

	pushfl
	pushl %es
	pushl %esi

	movw ARG1,%ax // set the selector
	movw %ax,%es

	movl ARG2,%eax
	movl %eax,%esi // init source index with offset
	movl %es:0(,%esi,1),%eax
	
	popl %esi
	popl %es
	popfl

	LEAVE

////
// void __writeb(unsigned short selector, unsigned int offset, unsigned char value)
////
ASM_PROC_FRAME(__writeb)
	ENTER

	pushfl
	pushl %eax
	pushl %es
	pushl %edi

	movw ARG1,%ax // set the selector
	movw %ax,%es

	movl ARG2,%eax
	movl %eax,%edi // init destination index with offset

	movb ARG3,%al
	movb %al,%es:0(,%edi,1)
	
	popl %edi
	popl %es
	popl %eax
	popfl

	LEAVE

////
// void __writew(unsigned short selector, unsigned int offset, unsigned short value)
////
ASM_PROC_FRAME(__writew)
	ENTER

	pushfl
	pushl %eax
	pushl %es
	pushl %edi

	movw ARG1,%ax // set the selector
	movw %ax,%es

	movl ARG2,%eax
	movl %eax,%edi // init destination index with offset

	movw ARG3,%ax
	movw %ax,%es:0(,%edi,1)
	
	popl %edi
	popl %es
	popl %eax
	popfl

	LEAVE

////
// void __writel(unsigned short selector, unsigned int offset, unsigned int value)
////
ASM_PROC_FRAME(__writel)
	ENTER

	pushfl
	pushl %eax
	pushl %es
	pushl %edi

	movw ARG1,%ax // set the selector
	movw %ax,%es

	movl ARG2,%eax
	movl %eax,%edi // init destination index with offset

	movl ARG3,%eax
	movl %eax,%es:0(,%edi,1)
	
	popl %edi
	popl %es
	popl %eax
	popfl

	LEAVE

////
// unsigned long int __getClockCount()
////
ASM_PROC_FRAME(__getClockCount)
	ENTER

	pushfl

	rdtsc // Get time stamp

	popfl
	
	LEAVE

////
// void __enableClockCountForUser()
////
ASM_PROC_FRAME(__enableClockCountForUser)
	ENTER

	pushfl
	pushl %eax

	movl %cr4,%eax
	andl $(~4),%eax // clear TSD(bit 2) of CR4 register
	movl %eax,%cr4 // TSD = Time Stamp Disable

	popl %eax
	popfl
	
	LEAVE

////
// void __disableClockCountForUser()
////
ASM_PROC_FRAME(__disableClockCountForUser)
	ENTER

	pushfl
	pushl %eax

	movl %cr4,%eax
	orl  $4,%eax // set TSD(bit 2) of cr4 register
	movl %eax,%cr4 // TSD = Time Stamp Disable

	popl %eax
	popfl
	
	LEAVE

ASM_PROC_FRAME(__wait)   
        pushl   %ecx
        movl    $(64*6000),%ecx
_w2:
        loop    _w2
        popl    %ecx
        ret




