;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;; Module de gestion de la mémoire en mode protégé. ;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;

; voir le fichier «mem386.doc» pour la documentation...

; [i] Macros en mode réel
; =======================

; Contrôle de la ligne A20
; ------------------------

;;;; Constantes pour accéder à la ligne A20
KB_command_port	= 64h		;(en écriture)
KB_status_port	= 64h		;(en lecture)
KB_data_port	= 60h
IB_free		= 2		;pour savoir si le port clavier est libre
KB_command_A20	= 0D1h		;pour accéder au port de la ligne A20
set_A20_ON  = 11011111b
set_A20_OFF = 11011101b

;;;; Macros pour contrôler la ligne A20

Macro	def_proc_KB_Ready  distance,erreur,nombre_essai
  local wait_IB
  ifdifi <distance>,<inline>
    Proc KB_Ready distance
  endif
 ;; Tester si le port clavier est disponible (chose nécessaire
 ;; avant de lui envoyer des commandes)
 ;; - erreur doit être une étiquette ou se brancher si la ligne
 ;; n'est pas disponible.
 ;; - nombre_essai contient le nombre d'essais d'accès au port avant de
 ;; s'avouer vaincu.
 ;; ** détruit AX,CX
   ifnb <&nombre_essai>
     ifdifi <nombre_essai>,<cx>
       mov cx,nombre_essai
     endif
   else
     mov cx,100
   endif
  wait_IB:
   in al,KB_status_port
   test al,IB_free
   loopne wait_IB
   jcxz erreur
  ifdifi <distance>,<inline>
	RET
    EndP  KB_Ready
  endif
EndM   def_proc_KB_Ready

Macro  set_A20	ON_OFF
 ;;  Cette macro active ou désactive la ligne A20, en ayant testé que le port
 ;; clavier est disponible.
 ;; - ON_OFF contient ON ou OFF selon que l'on veuille activer ou désactiver
 ;; la ligne. Si ce paramètre est absent, AH doit contenir A20_ON ou A20_OFF.
 ;; - erreur doit être une étiquette où se brancher si la ligne
 ;; n'est pas disponible.
 ;; - nombre_essai contient le nombre d'essais d'accès au port avant de
 ;; s'avouer vaincu.
 ;; ** détruit AX
  ifnb <&ON_OFF>
    mov ah,A20_&ON_OFF
  endif
  call KB_Ready
  mov al,KB_command_A20
  out KB_command_port,al
  ;call KB_Ready	; Linus does it
  mov al,ah
  out KB_command_port,al
  ;call KB_Ready	; Linus does it
EndM  set_A20

Macro	Test_A20
   local Exit_
   ;;  Teste si la ligne A20 est activée, en vérifiant si oui ou non les
   ;; adresses mémoire s'enroulent après la limite du méga-octet.
   ;; * Renvoie C ssi la ligne est activée
   ;; ** détruit AX
   push ds
   push es
   cli
   sub ax,ax
   mov es,ax		; es=$0000
   dec ax
   mov ds,ax		; ds=$FFFF
   mov ax,ds:[-2]	;; vérifier [$10FFEE]=[$0FFEE]
   cmp ax,es:[-18]
   stc
   jnz Exit_
   inc ds:[-2]		;; vérifier [$10FFEE]=[$0FFEE] après modification
   cmp ax,es:[-18]
   mov ds:[-2],ax
   stc
   jz Exit_
   clc
  Exit_:
   sti
   pop es
   pop ds
EndM Test_A20

Macro Disable_NMI
	mov al,80h
	out 70h,al
	in al,71h
EndM  Disable_NMI

Macro Enable_NMI
	mov al,00h
	out 70h,al
	in al,71h
EndM  Enable_NMI



; Le standard XMS
; ---------------

Struc	XMS_Move_Params		;; paramètres pour la fonction XMS 0Bh
   LengthToMove	dd	?
   SourceHandle	dw	?
   SourceOffset	dd	?
   DestHandle	dw	?
   DestOffset	dd	?
EndS	XMS_Move_Params


; Déterminer la nature du processeur
; ----------------------------------

Macro	def_proc_TestCPU  distance
  ifdifi <distance>,<inline>
    proc TestCPU distance
  endif

  dataseg
    NDP_STATUS dw -1
  codeseg

  ;;;; Procédure prise à Robert L. Hummel dans son bouquin sur les 80x86
  ;
  ; AH
  ; 01	8088 ou 8086
  ; 02	80286
  ; 03	80386
  ; 04	486
  ;
  ; AL
  ; 00	Pas de coprocesseur
  ; 01	8087
  ; 02	80287
  ; 03	80387
  ; 04	486DX ou 487SX
  ;
  ; Note: 0400 <=> 486SX sans copro.
  ;;;;;;
  ; détruit tous les registres de calcul, registres copro compris.
P8086
	MOV	DX,0100H	; réponse par défaut
	MOV	[NDP_STATUS],-1 ; non zéro
  ;; sur 8088/8086, les bits 12 à 15 de la valeur de Flags pushée sont mis à
  ;; 1 par PUSHF; c'est pourquoi on va essayer de les effacer.
	PUSHF
	POP	AX
	AND	AH,0FH		;(effacer les bits de poids fort)
	PUSH	AX
	POPF
	PUSHF
	POP	AX
	AND	AH,0F0H		;(les récupérer)
	CMP	AH,0F0H
	JZ	CPUID_2		; si oui, 8086/8088
  ;; sur le 80286, au contraire, ils sont effacés
	INC	DH
	PUSHF
	POP	AX
	OR	AH,0F0H		;(mettre les bits de poids fort)
	PUSH	AX
	POPF
	PUSHF
	POP	AX
	AND	AH,0F0H		;(les récupérer)
	JZ	CPUID_2		; si oui, 80286
  ;; sur le 80486, le registre EFLAGS dispose d'un bit d'alignement AC.
P386
	INC	DH
	CLI
	MOV	BP,SP		; sauver SP
	AND	SP,NOT 3	; aligner SP
	PUSHFD
	POP	EAX
	MOV	EBX,EAX		; sauver EFLAGS
	XOR	EAX,00040000H	; complémenter AC
	PUSH	EAX
	POPFD
	PUSHFD
	POP	EAX
	PUSH	EBX
	POPFD			; restaurer AC
	MOV	SP,BP		; restaurer la pile
	XOR	EAX,EBX		; regarder si AC a été modifié
	STI
	JZ	CPUID_2		; c'est un 386
	INC	DH
  ;;;; Maintenant, tester le coprocesseur:
CPUID_2:
	FNINIT				;réinitialiser le mot d'état
	FNSTSW	[NDP_STATUS]
	CMP	[Byte ptr NDP_STATUS],0	;si non nul, pas de copro
	JNZ	CPUID_3
	FNSTCW	[NDP_STATUS]		;tenter d'écrire un mot de contrôle
	AND	[NDP_STATUS],103FH
	CMP	[NDP_STATUS],3FH
	JNZ	CPUID_3
  ;;;; le seul cas où le copro peut ne pas correspondre, c'est quand un
  ;;;; 80287 est avec un 80386
	MOV	DL,DH			;initialiser le type de NDP
	CMP	DH,3			;pas 386 => ok
	JNZ	CPUID_3
  ;;; 387/287: le NDP (initialisé) doit gérer les infinis. Mais le 287 les
  ;;; confond.
	FLD1
	FLDZ
	FDIV
	FLD	ST
	FCHS
	FCOMPP
	FSTSW	[NDP_STATUS]
	MOV	AX,[NDP_STATUS]
	SAHF
	JNZ	CPUID_3			;différend:387
	DEC	DL
CPUID_3:
	MOV	AX,DX
  ifdifi <distance>,<inline>
	RET
    EndP  TestCPU
  endif
EndM	def_proc_TestCPU



; Gestion de la mémoire en mode protégé
; =====================================

Macro	DefDescriptor	bas,Lim,_P_DPL_DT_Type,_G_D_0_AVL
    dw  (Lim) and 0FFFFh
    dw	(bas) and 0FFFFh
    db	( (bas) shr 16 ) and 0FFh
    db	_P_DPL_DT_Type
    db	(_G_D_0_AVL) shl 4 + ( ( (Lim) shr 16) and 0Fh )
    db	(bas) shr 24
EndM

Macro	DefPorte	sel,ofs,compteur,_P_DPL_DT_Type
    dw  (ofs) and 0FFFFh
    dw	sel
    db	compteur
    db	_P_DPL_DT_Type
    dw (ofs) shr 16
EndM

Macro	SetDescriptor	DESCRIPT
   ;;; Cette macro initialise un descripteur avec
   ;;; - DESCRIPT contenant la chaîne adresse du descripteur
   ;;; - ebx pour adresse de base
   ;;; - ecx pour champ limite
   ;;; - al pour les champs d'information: P,DPL,DT,Type
   ;;; - ah pour G,D,AVL
   ;;; * détruit eax,ebx,ecx
   mov [DESCRIPT],cx
   shr ecx,16
   mov [DESCRIPT+2],bx
   shr ebx,16
   or cl,ah
   mov ch,bh
   mov bh,al
   mov [DESCRIPT+4],bx
   mov [DESCRIPT+6],cx
EndM

Macro	GetDescriptor	DESCRIPT
   Local
   ;;; Cette macro fait le contraire de la précédente:
   ;;; - DESCRIPT contient la chaîne adresse du descripteur
   ;;; - ebx pour adresse de base
   ;;; - ecx pour champ limite
   ;;; - al pour les champs d'information: P,DPL,DT,Type
   ;;; - ah pour G,D,AVL
   mov cx,DESCRIPT[6]
   mov bx,DESCRIPT[4]
   mov al,bh
   mov bh,ch
   mov ah,cl
   and cx,0Fh
   shl ebx,16
   mov bx,DESCRIPT[2]
   shl ecx,16
   mov cx,DESCRIPT
EndM

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; TSS ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Struc	pushad_s
	irp   parm,<edi,esi,ebp,esp,ebx,edx,ecx,eax>
          pushad_&parm  dd  ?
	endm
EndS	pushad_s
Struc	pusha_s
	irp   parm,<di,si,bp,sp,bx,dx,cx,ax>
          pusha_&parm  dw  ?
	endm
EndS	pusha_s

Struc	TSS16
;;;; spécifications Intel
    Sel_Lien_Arriere dw ?
    SP_CPL0	dw	? ; not updated by task switching
    SS_CPL0	dw	? ;
    SP_CPL1	dw	? ;
    SS_CPL1	dw	? ;
    SP_CPL2	dw	? ;
    SS_CPL2	dw	? ;
    saveIP	dw	?
    saveFlags	dw	?
    saveAX	dw	?
    saveCX	dw	?
    saveDX	dw	?
    saveBX	dw	?
    saveSP	dw	?
    saveBP	dw	?
    saveSI	dw	?
    saveDI	dw	?
    saveES	dw	?
    saveCS	dw	?
    saveSS	dw	?
    saveDS	dw	?
    saveLDT	dw	? ; not updated by task switching
EndS

Struc	TSS32
;;;; spécifications Intel
    Sel_Lien_Arriere dw ?,0	;(???)
    ESP_CPL0	dd	?	; not updated by task switching
    SS_CPL0	dw	?,0	;
    ESP_CPL1	dd	?	;
    SS_CPL1	dw	?,0	;
    ESP_CPL2	dd	?	;
    SS_CPL2	dw	?,0	;
		dd	0	;(reserved)
    saveEIP	dd	?
    saveEFlags	dd	?
    saveEAX	dd	?
    saveECX	dd	?
    saveEDX	dd	?
    saveEBX	dd	?
    saveESP	dd	?
    saveEBP	dd	?
    saveESI	dd	?
    saveEDI	dd	?
    saveES	dw	?,0	; 0's not updated by task switching
    saveCS	dw	?,0
    saveSS	dw	?,0
    saveDS	dw	?,0
    saveFS	dw	?,0
    saveGS	dw	?,0
    saveLDT	dw	?,0	; not updated by task switching
    TrapBit	dw	0	; not updated by task switching
    IO_Map_Base	dw	?	; not updated by task switching
EndS

PAGESIZE = 4096
PAGEBITS = 12