%include "asm.inc"
%define STUB_STAT   _stub_stat
	BITS 32
	GLOBAL entry_point
        GLOBAL __exit
        GLOBAL __fpu_family
        GLOBAL __cpu_family
        GLOBAL __cpu_model
        GLOBAL __cpu_steppingID
        GLOBAL __cpu_feature
        GLOBAL __cpu_vendor
        GLOBAL __cr4
        EXTERN _start
        EXTERN STUB_STAT
        EXTERN end_datas
        EXTERN end_bsss
	SECTION .data
	temp dw 0
        __fpu_family   db 0
	__cpu_family db 3
        __cpu_model  db 0
        __cpu_steppingID db 0
        __cpu_feature dd 0
        __cpu_vendor_  db 'unknown',0,0,0,0,0,0
        __cpu_vendor  dd __cpu_vendor_
        __cr4	     dd 0
        saved_ss_esp dd 0
	             dw 0
        saved_gdt    dw 0
                     dd 0
        SECTION .text
;
; questo e` l'indirizzo che viene chiamato dal loader.
; la paginazione e` gia` attiva, occhio pero` che per risparmiare
; memoria ho messo il pde e i pte nella memoria video :)
;
; secondo il draft del 10/09/197 il kernel dovrebbe essere caricato
; a 512Mb, e la memoria kernel e` riservata fino a 1Gb.
; quindi ci sono 512Mb di spazio per il kernel, dovrebbero essere
; sufficiente per tutti ... [come disse l'amico Bill per i 640K del dos]
;
; la versione 0.6 dello stub comunque mappa solo i primi 4Mb a partire
; dall'indirizzo virtuale base del kernel, e poi mappa il primo Mb 1:1.
;
;
; i selettori sono :
;   30h punta allo stub (256 bytes)
;   38h dati (limite 4G)
;   40h codice (4G)
;   48h TSS _TEMPORANEO_ (non deve essere + richiamato!)
;
;   DS = ES = SS = dati [30h]
;   FS = stub selector GS = ?
;
;   TR=48h, ldt=0, idt=0
;
;   esp = stack dello stub, poco meno di 1K disponibile ... magari usero`
;         un po' di memoria video anche per questo :)
;
;   tutti i flags sono stati azzerati (DF,IF,..)
;
;-----
; la versione 0.61 dello stub mappa i primi 12Mb di memoria fisica, e 4 kernel
; inoltre l'indirizzo del kernel e` cambiato, ora e` 3Gb, la memoria da
; 1Gb fino a 3Gb e` disponibile. la memoria < 1Gb e` riservata per altri scopi
entry_point:
        ;
        ; salva lo stack
        ;
        mov	dword [saved_ss_esp],esp
        mov     word [saved_ss_esp+4],ss
        ;
        ; salva gdt
        ;
        sgdt    [saved_gdt]
        ;
        ; azzera bss
        ;
        mov	edi,end_datas
        mov	ecx,end_bsss
        xor	eax,eax
        sub	ecx,edi
        rep	stosb
        ;
        ; copia stub_stat
        ;
        xor	esi,esi
        cmp	word [fs:esi],'V0'		; confronta magic (versione)
        mov	al,0xff
        jne	__exit
        mov	cx,[fs:esi+2]	; legge dimensione da trasferire
        add	esi,4
        mov	edi,STUB_STAT
.copy
        mov	al,[fs:esi]
        inc	esi
        mov	[edi],al
        inc	edi
        loop	.copy,cx
        mov	fs,cx		; azzera fs e gs
        mov	gs,cx
        call	cpu_info
        call	fpu_info
        cmp	byte [__fpu_family],0
        setne	al
        or	byte [__cpu_feature],al
        call	set_cr
        mov	bx,0xF8F0
        mov	cl,3
        call	pic_setup
	call	_start
__exit:
	; eax = exit
        cli
        mov	ebp,eax
        mov	bx,0x7008
        mov	cl,1
        call	pic_setup
        mov	bx,KERNEL_DS
        mov	ds,bx
;        cmp	dword [__cr4],0
;        je	.nocr4
;        xor	ebx,ebx
;        mov	cr4,ebx
.nocr4
        lgdt	[saved_gdt]		; DS deve sempre essere 30h !!
        lss	esp, [saved_ss_esp]
        mov	eax,ebp
        retf
;
; in: copia il bit %1 di edx alla posizione %2 in ebx
;
;
%macro testbt 2
        xor	ecx,ecx
	bt	edx,%1
        rcl	ecx,%2+1
        or	ebx,ecx
%endm
set_cr:
        ;
        ; setup per cr4 (registro ebx)
        ;
        mov	edx,[__cpu_feature]
        xor	ebx,ebx
        testbt	1,0		; VM86 Mode Ex
        testbt  1,1             ; PVI
        testbt  2,3		; Debug Extension
        testbt  3,4             ; Page Size extension
        testbt  13,7		; Page Global
        test	ebx,ebx
        jz	.nocr4
        mov	cr4,ebx
        mov	[__cr4],ebx
.nocr4
	;
        ; setup per cr0
        ;
	mov	ebx,0x80000001
        testbt	0,1
        cmp	byte [__cpu_family],4
        jb	.end
        je	.486
	or	ebx,50020h	; AM WP NE
.486
	or	ebx,50000h      ; AM WP
.end
	mov	cr0,ebx
        ret
cpu_info:
        pushfd
        pop	eax
        mov	eax,ecx
        xor	eax,0x40000
        push	eax
        popfd
        pushfd
        pop	eax
        xor	eax,ecx
        and	eax,0x40000
        jz	.end
        mov	byte [__cpu_family],4	; 486
        mov	eax,ecx
        xor	eax,0x200000
        push	eax
        popfd
        pushfd
        pop	eax
        xor	eax,ecx
        jz	.end
        push	ecx           		; 486DX4/Pentium+
        popfd
        xor	eax,eax
        cpuid
        mov	[__cpu_vendor_],ebx
        mov	[__cpu_vendor_+4],edx
        mov	[__cpu_vendor_+8],ecx
        test	eax,eax
        jz	.end
        mov	eax,1
        cpuid
        mov	cl,al
        and	cl,0x0f
        mov	[__cpu_steppingID],al
        shr	al,4
        mov	[__cpu_model],al
        mov	cl,ah
        and	cl,0x0f
        mov	[__cpu_family],cl
        mov	[__cpu_feature],edx
.end
	ret
fpu_info:
	clts
        fninit
        mov     word [temp],0x5a5a
        fstsw   [temp]
        mov	ax,[temp]
        cmp	al,0
        jnz	.end
        mov	byte [__fpu_family],2
        mov	eax,cr0
        shr	eax,5
        jnc	.end
        inc	byte [__fpu_family]
.end
	ret
;
; BL = PIC #1 vect
; BH = PIC #2 vect
; CL = 1 -> standard, 3 -> auto EOI
%macro delay 0
	dw 00ebh
        dw 00ebh
%endm
pic_setup:
	mov	al,0x11		;init command
	out	0x20,al
        delay
        out     0xa0,al
        mov	ax,bx  		; set vector
        out	0x21,al
        xchg	al,ah
        delay
        out	0xa1,al
        mov	al,4
        out	0x21,al		; irq 2 = cascade
        mov	al,2
        delay
        out	0xa1,al		; irq 9 ->irq2
        mov	al,cl		; mode control, 1 = standard, 3 auto EOI
        out	0x21,al
        delay
        out	0xa1,al
        ;
        ; keyboard reset
        ;
        delay
        in	al,60h
        delay
        or	al,80h
        out	60h,al
        delay
        and	al,7fh
        out	60h,al
        ret