#include <stdio.h>
#include <dos.h>
#include <time.h>

#define ATTR 0x7900       /* attributo di colore grigio su blu */

#define FMAX        1193180                /* frequenza massima per PIT */
#define NEWTICKms   5                      /* nuovo periodo di clock */
#define DIVISORE    FMAX*NEWTICKms/1000    /* calcolo il divisore */
#define NUMTICK55ms 55/NEWTICKms           /* ogni quanti tick vecchio 08 */
#define NUMTICK1s   1000/NEWTICKms         /* quanti tick per 1 secondo   */

/*-------------------------- TIPI DI DATI -------------------------------*/
typedef unsigned char BYTE;
typedef unsigned int WORD;
typedef unsigned int (far *s_arrayptr);     /* puntatore far */
/*-------------------------- VARIABILI PUBLIC ---------------------------*/
BYTE i;                 /* indice per for */
BYTE mask8253;
int  nticksec = 0;      /* contatore per i secondi            */
int  ntick    = 0;      /* contatore per chiamare vecchio 08h */
s_arrayptr screen[80];  /* schermo testo a colori             */
struct time t;          /* ora corrente                       */
char   ora[8];          /* ora corrente                       */


/*-------------------------- FUNZIONI -----------------------------------*/
void interrupt ( *Old08)(void);
void interrupt New08(void);

/**************************************************************************
	PinoContini                                                 | ProgramPIT
---------------------------------------------------------------------------
 La seguente funzione serve a variare la frequenza del timer di sistema
 8253 sul canale 0 IRQ0. In questo modo cambia la frequenza con cui viene
 chiamato INT 08H. La nuova frequenza si ottiene inviando al timer il
 divisore che si ottiene dalla seguente formula:
												 1 193 180 ---->(FMAX)
			nuovo divisore =  -----------
												freq. desid.

 Il divisore di default  65535 in modo che la frequenza sia 18.2 Hz cio
 ogni 55ms. Questa frequenza  da tenere da conto inquanto INT 08H originale
 serve a tenere aggiornato l'orologio del sistema e a controllare il motore
 del HD quindi qualsiasi sia la frequanza programmata il nuovo interrupt
 deve essere in grado di lasciare eseguire ogni 55 ms l'interrupt originale
 Questa routine inoltre cambia anche INT08H in modo che alla frequenza
 stabilita venga eseguita la funzione utente desiderata NewInt08 di seguito
 programmata.
--------------------------------------------------------------------------*/
void ProgramPIT(void)
{
	BYTE mask;
	BYTE mask8259;     /* byte di controllo per Prog.Inter.Contr. (PIC) */

	disable();                  /* disabilito interrupt */

	Old08 = getvect(0x08);      /* memorizzo il vecchio interrupt     */
	ntick = 0;                  /* azzero il contatore dei nuovi tick */

	mask8253 = inp(0x43);       /* salvo il valore per uninstall */

	/* ora aumento la frequenza del timer 8253 */
	mask = 0x36;  /* 0011 0110B  00xx xxxx  = canale 0
															 xx11 xxxx  = prima LSB poi MSB
															 xxxx 011x  = modo 3 (onda quadra)
															 xxxx xxx0  = sistema binario
								*/

	_AX = DIVISORE;

	outp(0x43,mask);       /* parola di controllo al registro dell'8253 */
	outp(0x40,_AL);        /* invio LSB all'8253 canale 0 */
	outp(0x40,_AH);        /* invio MSB all'8253 canale 0 */

	/* ora spengo tutti gli interrupt mentre cambio INT 08H */
	mask8259 = inp(0x21);      /* legge lo stato degli interrupt del PIC */
	outp(0x21,0xFF);           /* spegno tutti IRQ0..IRQ7 */
	setvect(0x08, New08);      /* imposto il nuovo interrupt */
	outp(0x21,mask8259);       /* ripristino il registro PIC */

	enable();                  /* abilito gli interrupt */
}

/**************************************************************************
	PinoContini                                                 | New08
----------------------------------------------------------------------------
DESCRIZIONE: questa funzione viene chiamata ogni tick ms da clock del 8253.
	Verifico poi che non siano trascorsi 55ms ed eventualmente attivo il
	vecchio int08h. Questo invia direttamente EOI al PIC.
	NOTA: la somma di un numero 0..9 con '0'  pari al valore ASCII del numero
	inoltre si ricorda che 3 % 10 = 3 e 24 % 10 = 4.
	ancora 47 / 10 = 7    22 / 10 = 2 (la parte decimale viene troncata )
PARAMETRI:
RITORNA:
--------------------------------------------------------------------------*/
void interrupt New08(void)
{

	nticksec++;                   /* incremento il contatore per i secondi */
	if(nticksec == NUMTICK1s)     /* se trascorso un secondo */
	{
		nticksec = 0;              /*  azzero il contatore tick per secondi */
		t.ti_sec++;                /* incremento i secondi */
		if(t.ti_sec == 60)         /* se trascorso un minuto */
		{
			t.ti_sec = 0;            /* azzero i secondi */
			t.ti_min++;              /* incremnto i minuti */
			if(t.ti_min==60)         /* se passata un'ora  */
			{
				t.ti_min = 0;          /* azzero i minuti */
				t.ti_hour++;           /* incrememto le ore */
				if(t.ti_hour == 24)    /* se 24 ore */
				{
					t.ti_hour = 0;       /* azzero le ore */
				}
				ora[0] = t.ti_hour / 10  + '0';  /* se nuova ora aggiorno */
				ora[1] = t.ti_hour % 10  + '0';
			}
			ora[3] = t.ti_min / 10  + '0';   /* se nuovo minuto aggiorno */
			ora[4] = t.ti_min % 10  + '0';
		}

		ora[6] = t.ti_sec / 10  + '0';  /* aggiorno nuovo secondo */
		ora[7] = t.ti_sec % 10  + '0';

		for(i=0;i<8;i++) screen[0][72+i] = ora[i]+ATTR;  /* mostro a video */
	}

	ntick++;                  /* incremento il numero dei tick */
	if(ntick == NUMTICK55ms)  /* se trascorsi 55ms */
	{ ntick = 0;              /* azzero il contatore */
		Old08();                /* chiamo il vecchio int 08 */
	} else outp(0x20,0x20);   /* altrimenti invio EOI al PIC */
}

/***************************************************************************
	MAIN
--------------------------------------------------------------------------*/
void main(void)
{

	nticksec = 0;
	screen[0]     = (s_arrayptr) MK_FP(0xB800,0);

	gettime(&t);
	ora[0] = t.ti_hour / 10  + '0';
	ora[1] = t.ti_hour % 10  + '0';
	ora[2] = ':' ;
	ora[3] = t.ti_min / 10  + '0';
	ora[4] = t.ti_min % 10  + '0';
	ora[5] = ':' ;
	ora[6] = t.ti_sec / 10  + '0';
	ora[7] = t.ti_sec % 10  + '0';
	for(i=0;i<8;i++) screen[0][72+i] = ora[i]+ATTR;

	ProgramPIT();
	keep(0, (_SS + 16 + (_SP/16) - _psp));

}