/*
  A simple protected mode priority based multitasking kernel for
  Intel 386/486/Pentium processors

  Written by Tuomo Kortesmaa 1995
  Copyright (c) 1995 Tuomo Kortesmaa (Email: tk@evitech.fi)
  Some constants (and also some ideas) to set up devices are found
  from an assembly code material of Dennis Marer- many thanks to him!

  
  "The product" is intended only for teaching and demonstration purposes
  and has no warranty.

*/

#define DPL_SYS 0       /* System privilege level */
#define DPL_USER        3       /* User privilege level */
#define PRESENT 1       /* Segment is present in memory */
#define SEG_SYS 0       /* Segment is system*/
#define SEG_DC  1       /* Segment is data or code */
#define TYPE_DATA       3       /* Descriptor type fields */
#define TYPE_CODE       0x0f    /* b?*/
#define TYPE_TSS        0x09    /* b!!*/
#define TYPE_LDT        0x02    /* a??*/
#define TYPE_INT        0x0e    /*e  */
#define TYPE_TASK_GATE  0x05
#define X_BIT_C 0       /*1   X- bit code */
#define X_BIT_D       0       /*1   X- bit data */
#define X_BIT_LDT       0
#define X_BIT_TSS       0
#define NOFINTERRUPTS     47
#define TSS_DESCR_BASE  6       /* Start of task state segment descriptors in GDT*/

#define DPL_3_STACK_OFFSET      1024
//!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
/*=============================================================================
;  The 386/486 Protected Mode structures and definitions.
;
;
; Description:
;  This file defines the structures and equates for use in setting up and
;  using the 386 Protected Mode.  This includes all types of descriptors,
;  Task State Segments (TSS).
;
; Portability:
;  Requires BORLAND C++ 3.1  or better to be compiled.
;  Dependent on a 386 or later microprocessor.
;  No other platform or hardware dependencies.
;
;=============================================================================*/

/* Segment descriptor definition - see Intel literature  for other information */
typedef struct {

        unsigned int limit_0    : 16;   /* segment limit, 15-0 */
        unsigned int base_0     : 16;   /* segment base, 15-0 */
        unsigned int base_16    :  8;   /* segment base, 23-16 */
        unsigned int type       :  4;   /* type */
        unsigned int segment    :  1;   /* segment */
        unsigned int DPL        :  2;   /* Descriptor Privilege Level */
        unsigned int present    :  1;   /* segment present bit */
        unsigned int limit_16   :  4;   /* segment limit, bits 19-16 */
        unsigned int avail      :  1;   /* available bit */
        unsigned int unused     :  1;   /* unused bit */
        unsigned int X          :  1;   /* X bit */
        unsigned int granul     :  1;   /* granularity bit */
        unsigned int base_24    :  8;   /* segment base, bits 31-24 */

} SEGMENT_DESCR;

/* Selector definition - see Intel literature  for other information */
typedef int SELECTOR;

typedef struct {

        unsigned int offset_0   : 16;   /* offset, 15-0 */
        unsigned int selector   : 16;   /* segment selector */
        unsigned int            :  5;   /* word count */
        unsigned int            :  3;   /* unused */
        unsigned int type       :  5;   /* descriptor type */
        unsigned int DPL        :  2;   /* Descriptor Privilege Level */
        unsigned int present    :  1;   /* segment present bit */
        unsigned int offset_16  : 16;   /* segment limit, bits 19-16 */

} GATE_DESCR;

/*-----------------------------------------------------------------------------
;  tss386 - A 386 Protected Mode Task State Segment.  Any amount of data may
;  exist between the I/O bitmap offset value and the I/O bitmap, definable
;  by the operating system.  Here, no extra memory is assumed, and the
;  I/O bitmap is fully defined (8192 bytes) for full access.
;-----------------------------------------------------------------------------*/
/*Task State Segment  definition - see Intel literature  for other information */
typedef struct {
        unsigned int    t3BackLink      : 16;   //** TSS backlink
        unsigned int    d1              : 16;   //** Reserved
        unsigned long   t3ESP0;                         //** Stack 0 pointer (CPL=0)
        unsigned int    t3SS0           : 16;   //** Stack 0 segment
        unsigned int    d2              : 16;   //** Reserved
        unsigned long   t3ESP1;                         //** Stack 1 pointer (CPL=1)
        unsigned int    t3SS1           : 16;   //** Stack 1 segment
        unsigned int    d3              : 16;   //** Reserved
        unsigned long   t3ESP2;                         //** Stack 2 pointer (CPL=2)
        unsigned int    t3SS2           : 16;   //** Stack 2 segment
        unsigned int    d4              : 16;   //** Reserved
        unsigned long   t3CR3;                          //** CR3
        unsigned long   t3EIP;                 //** Instruction pointer
        unsigned long   t3EFlags;               //** Extended flags
        unsigned long   t3EAX;                          //** Task general registers
        unsigned long   t3ECX;
        unsigned long   t3EDX;
        unsigned long   t3EBX;
        unsigned long   t3ESP;
        unsigned long   t3EBP;
        unsigned long   t3ESI;
        unsigned long   t3EDI;
        unsigned int    t3ES            : 16;   //** Task segment registers
        unsigned int    d5              : 16;   //** Reserved
        unsigned int    t3CS            : 16;
        unsigned int    d6              : 16;   //** Reserved
        unsigned int    t3SS            : 16;
        unsigned int    d7              : 16;   //** Reserved
        unsigned int    t3DS            : 16;
        unsigned int    d8              : 16;   //** Reserved
        unsigned int    t3FS            : 16;
        unsigned int    d9              : 16;   //** Reserved
        unsigned int    t3GS            : 16;
        unsigned int    d10             : 16;   //** Reserved
        unsigned int    t3LDT           : 16;   //** Task local descriptor table
        unsigned int    d11             : 16;   //** Reserved
        unsigned int    t3Trap          :  1;   //** Holds debug trap bit (T)
        unsigned int    d12             : 15;   //** Reserved
        unsigned int    t3IOOfs         : 16;   //** I/O map address offset
        unsigned int    t3IOMap[256];   //** Give full I/O access
        unsigned int    terminator      :  8;   //** Terminator 0FFh

} TSS_SEGMENT;
/* Not used */
typedef struct
 { int limit;
   long base;
 } INFO;     /* To be loaded to GDTR and IDTR */



/********************************************************************/
/*                                                                                                                                      */
/* FUNCTION PROTOTYPES                                                                                          */
/*                                                                                                                                      */
/********************************************************************/



#define  SelKernel             0x08     /* Kernel task state segment selector */
#define  SelDebug              0x0b0
#define  SelKCS        0x010    /* Kernel code segment selector */
#define  SelKDS        0x018    /* Kernel data and stack segment selector */
#define  SelScreen        0x020    /* Display memory segment selector */
#define  SelShm        0x028    /* Shared memory segment selector */
/*
  A simple protected mode priority based multitasking kernel for
  Intel 386/486/Pentium processors

  Written by Tuomo Kortesmaa 1995
  Copyright (c) 1995 Tuomo Kortesmaa (Email: tk@evitech.fi)
  Some constants (and also some ideas) to set up devices are found
  from an assembly code material of Dennis Marer- many thanks to him!

  Any license is not needed: If You find this material useful in any purpose,
  please send a short email notice to the author.
  All comments and corrections are appreciated.
  "The product" is intended only for teaching and demonstration purposes
  and has no warranty.


*/
#include "kernel.h"
/**************************************************************************/

void main ( )
{
        KERNELBASE   = (long)(16)*(long)(_CS);
        disable();     /* Interrupts are not allowed */
        asm pushfd;    /* Push 32- bit flag register */
        asm pop         eax;
        asm and         eax,0x0fff;
        asm push        eax;
        asm popfd;     /* Flags back */
        Setup_GDT();   /* Set up Global Descriptor Table */

        Setup_IDT();   /* Set up Interrupt Descriptor Table */
        Setup_SP ();   /* Set up Slave Processor 8042 */
        Setup_PIC();   /* Set up programmable interrupt controller */
        Setup_PIT();   /* Set up programmable interrupt timer */
        init_serial(); /* Set initialisation of the serial line */
        asm push SelKCS;      /* Set protected mode start point into stack*/
        _AX=(int)MainKernel;  /* Selector and offset*/
        asm push ax;
        DispInit();

        asm     {
                cli;
                lgdt    fword ptr GDTInfo;   /* Load GDT linear address into lgdt */
                lidt    fword ptr IDTInfo;   /* Load IDT linear address into lidt */
                mov     eax,cr0;
                or      al,0x01;             /* Protected mode bit into c0- register */
                mov     cr0,eax;             /* We are in protected mode */
                retf;                        /* Jump to the protected mode start address set earlier */
        }
}

//********************************************************//
//** Setup_GDT              1993                        **//
//**                                                    **//
//********************************************************//
//**                                                    **//
//** Luodaan Global Descriptors Table                   **//
//**                                                    **//
//********************************************************//
void Setup_GDT(void)
{
int                     loop;//,seg;
long int                diff;
SEGMENT_DESCR           *p_gdt = GDT;
SELECTOR                nullSel,SEL;
        _ES= 0x0b800;
        GDTInfo.limit= sizeof(GDT);                     /* Set linear addresses*/
        GDTInfo.base = (long)16*(long)_DS + (int)GDT;
        IDTInfo.limit= sizeof(IDT);
        IDTInfo.base = (long)16*(long)_DS + (int)IDT;
        nullSel      =  0;
        set_descriptor(p_gdt++,0L,0L,0,0,0,0,0);        /* First entry is null descriptor */
        Kernel_descr =p_gdt;
                                                         /* Second entry is kernel TSS descriptor*/
        set_tss_descriptor(p_gdt++,sizeof(TSS_SEGMENT)-1,
                    ( long)16*(long)_DS+(int)&TSS_Kernel,DPL_SYS);
        KCS_descr    = p_gdt;                            /* Kernel Code segment descriptor*/
        set_code_descriptor(p_gdt++,(long)SEGSIZE-1,KERNELBASE,DPL_SYS);
                                                        //** Kernel code & data descriptor
        KDS_descr = p_gdt;
        diff      = KERNELBASE - (long)(16)*(long)(_DS);
        set_data_descriptor(p_gdt++,(long)(SEGSIZE-1)-diff,(long)(16)*(long)(_DS),DPL_SYS);
                                                        /* Set kernel TSS */
        set_tss(&TSS_Kernel,SelKCS,SelKDS,
                           (long)(SEGSIZE-1)/*ERROR*/, nullSel);
        TSS_Kernel.t3EFlags     =       0x002;          /* Set tss is tailored for user we must correct */
        TSS_Kernel.t3ESP        =       0L;
        Screen_descr            =       p_gdt;
        set_data_descriptor(p_gdt++,0xffff,0x0B8000L,DPL_USER);
        Shm_descr = p_gdt;
        set_data_descriptor(p_gdt++,0xffff,0x0B8000L,DPL_USER);

        for (loop=0;loop < _MAXPROCESS;loop++)
        {
         TSS_descr[loop]=p_gdt;
         set_tss_descriptor(p_gdt++,sizeof(TSS_SEGMENT)-1,
                      (long)(16)*(long)_DS+(int)&TSS[loop], DPL_SYS);
         SEL          =  (SELBASE +0x20 *loop);  /* Selectors and descriptors for user processes */
         Sel_TSS[loop]=  (SEL-0x8);
         CODESEL      =  (SEL) | DPL_USER;
         DATASEL      =  (SEL +0x08) | DPL_USER;
         SelData[loop+1]=  DATASEL;
         set_tss(&TSS[loop],CODESEL, DATASEL,(long)(SEGSIZE-1)-OFF[loop],0);
         TSS[loop].t3GS  =  DATASEL+0x08;
         set_code_descriptor(p_gdt++,SEGSIZE-1,
                              KERNELBASE+(loop+1)*SEGSIZE,DPL_USER);

         set_data_descriptor(p_gdt++,SEGSIZE-1-OFF[loop],
                             KERNELBASE+(loop+1)*SEGSIZE+OFF[loop],DPL_USER);
         set_data_descriptor(p_gdt++,15,
                                  (long)(16)*(long)_DS+(int)&PCB[loop].CommData,DPL_USER);

        }
//_____________________DEBUG_____________TASK_____
        Debug_descr =p_gdt;
                                                         /* Second entry is kernel TSS descriptor*/
        set_tss_descriptor(p_gdt++,sizeof(TSS_SEGMENT)-1,
                    ( long)16*(long)_DS+(int)&TSS_Debug,DPL_USER);

        set_tss(&TSS_Debug,SelKCS,SelKDS,
                           (long)(SEGSIZE-1), nullSel);
       //       set_data_descriptor(p_gdt++,SEGSIZE-1,
       //                            KERNELBASE+ (_MAXPROCESS+1)*SEGSIZE,DPL_SYS);
        TSS_Debug.t3EFlags= 0x002;          /* Set tss is tailored for user we must correct */
        TSS_Debug.t3ESP   = 0x0ffff;
        TSS_Debug.t3EIP   = (unsigned int)DebugTask;
        TSS_Debug.t3SS0   = SelKDS;
        TSS_Debug.t3SS1   = SelKDS;
        TSS_Debug.t3SS2   = SelKDS;
//______________DEBUG____________________TASK______________
}
//********************************************************//
//** Setup_IDT             1993                         **//
//**                                                    **//
//********************************************************//

             set_gate_descriptor(&IDT[RECEIVE],
                  0L,SelKernel, TYPE_TASK_GATE,DPL_USER,PRESENT);
             set_gate_descriptor(&IDT[SEND],
                  0L,SelKernel,TYPE_TASK_GATE ,DPL_USER,PRESENT);
        /* Only those hw- interrupts are gated to kernel which can cause
           context switch */
        /* CLOCKINT stands here for any hardware int possible to cause context switch */
             set_gate_descriptor(&IDT[CLOCKINT],
                  0L,SelKernel,TYPE_TASK_GATE,DPL_USER,PRESENT);
             set_gate_descriptor(&IDT[DEBUGINT],
                  0L,SelDebug,TYPE_TASK_GATE,DPL_USER,PRESENT);

/* See Intel manuals for bitfields in descriptors */

int set_descriptor(     SEGMENT_DESCR   *segdesc,
                                        unsigned long   limit,
                                        unsigned long   base,
                                        unsigned int    type,
                                        unsigned int    segment,
                                        unsigned int    DPL,
                                        unsigned int    present,
                                        unsigned int    X
                                  )
{
        segdesc->limit_0 = limit &  0xffff; /* Set limit */
        segdesc->limit_16= limit >> 16;
        segdesc->base_0  =  base &  0xffff;
        segdesc->base_16 = (base &  0xff0000)   >> 16;
        segdesc->base_24 = (base &  0xff000000) >> 24;
        segdesc->type    = type ;
        segdesc->segment = segment ;
        /* Set Descriptor Privilege Level */
        segdesc->DPL     = DPL ;
        segdesc->present = present ;  /* Set present bit */
        segdesc->avail   = 0;
        segdesc->unused  = 0;
        segdesc->X       = X;
        segdesc->granul  = 0;

        return(TRUE);
}

//********************************************************//
//** set_gate_descriptor              1993              **//
//**                                                    **//
//********************************************************//
int set_gate_descriptor(GATE_DESCR              *gatedesc,
                                                unsigned long   offset,
                                                SELECTOR        selector,

                                                unsigned int    type,
                                                unsigned int    DPL,
                                                unsigned int    present

                                           )
{       long  int z;
        gatedesc->offset_0       =       offset & 0xffff;
        z                        =      (offset & 0x000f0000);
        gatedesc->offset_16      =      (int)( z >> 16);
        gatedesc->selector       =       selector;
        gatedesc->type           =       type ;
        gatedesc->DPL            =       DPL ;
        gatedesc->present        =       present;

        return(TRUE);
}

//********************************************************//
//** set-TSS-descriptor                                 **//
//**                                                    **//
//********************************************************//
int set_tss_descriptor(SEGMENT_DESCR    *segdesc,
                                           unsigned long        limit,
                                           unsigned long        base,
                                           unsigned int         dpl
                                          )
{
        set_descriptor(segdesc, limit, base, TYPE_TSS,SEG_SYS,dpl,PRESENT,X_BIT_TSS);
        return(TRUE);
}

//********************************************************//
//** set-code-descriptor                                **//
//**                                                    **//
//********************************************************//
int set_code_descriptor(SEGMENT_DESCR   *segdesc,
                                                unsigned long   limit,
                                                unsigned long   base ,
                                                unsigned int    dpl
                                          )
{
        set_descriptor(segdesc, limit, base,TYPE_CODE ,SEG_DC,dpl,PRESENT,X_BIT_C);
        return(TRUE);
}

//********************************************************//
//** set-data-descriptor                                **//
//**                                                    **//
//********************************************************//
int set_data_descriptor(SEGMENT_DESCR   *segdesc,
                                                unsigned long   limit,
                                                unsigned long   base,
                                                unsigned int    dpl

                                              )
{
        set_descriptor(segdesc, limit, base,TYPE_DATA ,SEG_DC,dpl,PRESENT,X_BIT_D );
        return(TRUE);
}


//********************************************************//
//** set-tss                                            **//
//**                                                    **//
//********************************************************//
int set_tss( TSS_SEGMENT  *tss_seg,
                         SELECTOR         codesel,
                         SELECTOR         datasel,
                         long             stack,
                         SELECTOR         sel_LDT
                   )
{
        tss_seg->t3BackLink     =0;                                                     //** TSS backlink
        tss_seg->d1             =0;                                                                     //** Reserved
        tss_seg->t3ESP0         =(unsigned long)stack;          //** Stack 0 pointer (CPL=0)
        tss_seg->t3SS0          =(unsigned int) datasel;        //** Stack 0 segment
        tss_seg->d2             =0;                                                             //** Reserved
        tss_seg->t3ESP1         =(unsigned long)stack;          //** Stack 1 pointer (CPL=1)
        tss_seg->t3SS1          =(unsigned int) datasel;        //** Stack 1 segment
        tss_seg->d3             =0;                                                                     //** Reserved
        tss_seg->t3ESP2         =(unsigned long)stack;          //** Stack 2 pointer (CPL=2)
        tss_seg->t3SS2          =(unsigned int)  datasel;       //** Stack 2 segment
        tss_seg->d4             =0;                                                             //** Reserved
        tss_seg->t3CR3          =0L;                                                    //** CR3
        tss_seg->t3EIP          =0L;                                                            //** Instruction pointer
        tss_seg->t3EFlags       =0x03202;                                       //** Extended flags, interrupts off
        tss_seg->t3EAX          =0L;                                                            //** Task general registers
        tss_seg->t3ECX          =0L;
        tss_seg->t3EDX          =0L;
        tss_seg->t3EBX          =0L;
        tss_seg->t3ESP          =(unsigned long)stack-DPL_3_STACK_OFFSET;
        tss_seg->t3EBP          =0L;
        tss_seg->t3ESI          =0L;
        tss_seg->t3EDI          =0L;
        tss_seg->t3ES           =(unsigned int) datasel;/*datasel;*/                    //** Task segment registers
        tss_seg->d5             =0;                                                                     //** Reserved
        tss_seg->t3CS           =(unsigned int) codesel;
        tss_seg->d6             =0;                                                             //** Reserved
        tss_seg->t3SS           =(unsigned int) datasel;
        tss_seg->d7             =0;                                                                     //** Reserved
        tss_seg->t3DS           =(unsigned int) datasel;
        tss_seg->d8             =0;                                                             //** Reserved
        tss_seg->t3FS           =(unsigned int) SelScreen;  /* Screen selector */
        tss_seg->d9             =0;                                                             //** Reserved
        tss_seg->t3GS           =(unsigned int) SelShm;  /* Shared memory selector */
        tss_seg->d10            =0;                                                             //** Reserved
        tss_seg->t3LDT          =(unsigned int) sel_LDT;                        //** Task local descriptor table
        tss_seg->d11            =0;                                                             //** Reserved
        tss_seg->t3Trap         =0;                                                     //** Holds debug trap bit (T)
        tss_seg->d12            =0;                                                             //** Reserved
        tss_seg->t3IOOfs        =sizeof(TSS_SEGMENT);                   //** I/O map address offset
        tss_seg->terminator     =0xff;                                          //** Terminator 0FFh

        return(TRUE);
}


/* Check have we now processes ready to run*/
while( ( AP >= 0 ) || ( ( ACOUNT > 0) && ( AP == -1 ) ) )
 {                              /* Either old active process " AP" or some else*/
 if( (ACOUNT > 0 ) && (  AP == -1 ) )
  {
  --ACOUNT;                    /* Decrement READY  process count */
  while(++AP <_MAXPROC)        /* Find highest priority ready process */
   if(PCB[AP].STATE == READY )break;  /* Finding high priority process takes*/
                               /* small amount of processing time */
  PCB[AP].STATE=RUNNING;
  TSS_Addr[1]  =Sel_TSS[AP];   /* Set Task state segment as jump adders */
  }
asm jmp dword ptr [TSS_Addr];/* Context switch is made by jump to TSS*/



/* Servicing of software system call "receive message"*/
void Receive(int Box,int Timeout)
 {
  if( MailBox[Box].WaitCount >0 || MailBox[Box].Head == MailBox[Box].Tail )/* Must wait? */
  {
   ++MailBox[Box].WaitCount;           /* We must wait- message (sleeping) */
   PCB[AP].TIMEOUT = TCOUNT + Timeout; /* Store timeout time */
   PCB[AP].MBptr   = Box;              /* Pointer to Box from PCB*/
   PCB[AP].STATE   = WAITING;          /* Process now waiting*/
   AP              = -1;               /* ACTIVE PROCESS IS NO MORE ACTIVE */
   Schedule();
}
 else { if( MailBox[Box].Head != MailBox[Box].Tail)
                PCB[AP].Message = GetBuff(&MailBox[Box]);
      } /* There was  message waiting */
}

/* system call send message service */
void Send(int Box, int Message)
{
 static iy,iold;
 
 if(  MailBox[Box].WaitCount > 0 )   /* There are waiting processes? */
  {
   MailBox[Box].WaitCount--;
   for( iy = 0 ;iy <_MAXPROC ; iy++) /* Seek highest priority waiting process */
   if(PCB[iy].MBptr == Box )
   {
    iold = iy;                       /* Save found process */
    iy   =_MAXPROC;                  /* Out of the loop */
    PCB[iold].STATE   = READY;       /* Found process ready to schedule */
    ACOUNT++;                        /* Increment active process count*/
    PCB[iold].TIMEOUT = -1;          /* Clear timeout */
    PCB[iold].Message = Message;     /* Give message to the waiting process */
    PCB[iold].MBptr   = -1;          /* Process is no more waiting any message queue*/
   }
   if( iold < AP )
      {
        PCB[AP].STATE = READY;     /* Active process is now only READY  */
        ACOUNT++;
        AP            = -1;        /* No active process */
           Schedule();            
      }
    
 }
else { PutBuff(&MailBox[Box],Message);
/* Save message in the circular buffer of the box */

}