; BA-PM.asm
; Protected-Mode code & data for the MOOSE BIOS-based API
; see ~/info/BIOS-API.nfo

Ideal
Include "../API.ash"
Include "BIOS-API.inc"

DEBUG = 1
__DEBUG__ = 1

;;;;;;;;;;;;;;;;;;;;;;;;;;;;; EXTRN statements ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Extrn	Init_RM_Target_seg : ABS
Extrn	Init_RM_Target_ofs : NEAR
Extrn	GoReal: NEAR

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Constants ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
__PMPART__	=	__PC__	; PM_Code segment comes first
BBO_BUFSIZE	=	256	; Buffer size for Buffered BIOS-based Console 
				; output.

;;;;;;;;;;;;;;;;;;;;;;;;;;; Global Declarations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
_Code_is	PM_Code
_Data_is	PM_Data
_UData_is	PM_UData
Assume cs:API,ds:API		; that's the default anyway
;Assume	cs:PM_Code,ds:PM_Code	; (!) Will that be ok ???

;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Global Variables ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_Data
__PD__:			; (!) must be first thing in PM_Data

label rm_idt_address qword
		dw	256*4-1	; Limit of real-mode IDT
		dd	0	; Base Address of real-mode IDT

label pm_idt_address qword
		dw	256*8-1	; Limit of protected-mode IDT
		dd	?	; Base Address of protected-mode IDT
EndS	PM_Data
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Uninitialized Data ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_UData
__PU__:			; (!) must be first thing in PM_UData
if1
RM_Part_PA	dd	?	; physical address of real-mode part
PM_Part_PA	dd	?	; physical address of protected-mode part
KERNEL_PA	dd	?	; physical address of Kernel.
label RM_Part_Addr dword
RM_Part_ofs	dw	?	; RM offset to RM part
RM_Part_seg	dw	?	; RM segment for RM part
PM_LDT_sel	dw	?	; Address of protected-mode LDT
CPU_Type	dw	?	; what processor/coprocessor.
align 16
__GDT__		dq	GDT_SIZE dup (?)
__IDT__		dq	IDT_SIZE dup (?)
__Stack__	dd	PM_STACK_SIZE dup (?)
__Stack_Top__	=	$
endif
EndS	PM_UData
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; IO Functions ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
IF 0
	;;;; Buffered BIOS-based Console output ;;;;
Segment	RM_UData
if1
BBO_Buffer	db	BBO_BUFSIZE dup (?)
endif
EndS	RM_UData
Segment RM_Data
BBO_BP		dd	0
EndS	RM_Data
Segment PM_Code
Proc BBPutc
	pushad
	mov edi,[BBO_BP]
	cld
	stosb
	@IF ae
	  cmp edi,BBO_BUFSIZE
	@THEN
	  call BBOFlush
	@ENDIF
	popad
	retn
EndP BBPutc
Proc BBOFlush
	mov eax,[RM_Part_Addr-__PD__]
	mov ax,offset RM_BBOFlush
	push eax
	jmp Go_BIOS_Real
EndP BBOFlush
EndS	PM_Code
ENDIF
;;;;;;;;;;;;;;;;;;;;;;;;;;;;; Hardware IO modes ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_Code
__PC__:			; (!) must be first thing in PM_Code
Proc	Set_MOOSE_IO_mode	near
;;; program I/O devices for MOOSE.
; PREconditions:
; * Interrupt,Global,Local,Page Tables have been initialized
;   IRQ's and NMI's are disabled (were disabled before RM->PM switching)
; POSTconditions:
; * IDT,GDT,LDT,TSS,CR3 registers are loaded.
;
  ;;; disable IRQ's
	cli
  ;;; disable NMI's
	call DisableNMI
  ;;; load protected-mode idt
	lidt [pm_idt_address]
EndP	Set_MOOSE_IO_mode
Proc	Set_BIOS_IO_mode	near
;;; reprogram I/O devices so the BIOS can use them
; Must be the exact converse of Set_MOOSE_IO_mode
;
; currently, reenable all interrupts
   ;;; load real-mode idt...
	lidt [cs:rm_idt_address]
   ;;; reenable NMI's
	call EnableNMI
   ;;; reenable IRQ's
	sti
   ;;; return to the caller
	ret
EndP	Set_BIOS_IO_mode
EndS	PM_Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; I/O subroutines ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_Code
Proc	Delay
   ;;; Delay between I/O, as in Linux.
	jmp short Delay_
 Delay_:
	ret
EndP	Delay
Proc	DisableNMI
	Disable_NMI
	ret
EndP	DisableNMI
Proc	EnableNMI
	Enable_NMI
	ret
EndP	EnableNMI
EndS	PM_Code
;;;;;;;;;;;;;;;;;;;;;;;;;; Initialization routines ;;;;;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_Code
Proc	Setup_PM_8259
;; Taken from Linux' boot/setup.S
	mov	al,11h		; initialization sequence
	out	020h,al		; send it to 8259A-1
	calls	Delay
	out	0A0h,al		; and to 8259A-2
	calls	Delay
	mov	al,IRQ_INT_BASE		; start of hardware int's (0x90)
	out	021h,al
	calls	Delay
	mov	al,IRQ_INT_BASE+8	; start of hardware int's 2 (0x98)
	out	0A1h,al
	calls	Delay
	mov	al,04h		; 8259-1 is master
	out	021h,al
	calls	Delay
	mov	al,02h		; 8259-2 is slave
	out	0A1h,al
	calls	Delay
	mov	al,01h		; 8086 mode for both
	out	021h,al
	calls	Delay
	out	0A1h,al
	calls	Delay
;; not needed: already all interrupts masked off.
;	mov	al,0FFh		; mask off all interrupts for now
;	out	0A1h,al
;	calls	Delay
;	mov	al,0FBh		; mask all irq's but irq2 which
;	out	021h,al			; is cascaded
	ret
EndP	Setup_PM_8259
Proc	Setup_RM_8259
	mov	al,11h		; initialization sequence
	out	020h,al		; send it to 8259A-1
	call	Delay
	out	0A0h,al		; and to 8259A-2
	call	Delay
	mov	al,08h		; start of hardware int's (0x08 argl !)
	out	021h,al
	call	Delay
	mov	al,70h		; start of hardware int's 2 (0x70)
	out	0A1h,al
	call	Delay
	mov	al,04h		; 8259-1 is master
	out	021h,al
	call	Delay
	mov	al,02h		; 8259-2 is slave
	out	0A1h,al
	call	Delay
	mov	al,01h		; 8086 mode for both
	out	021h,al
	call	Delay
	out	0A1h,al
	call	Delay
;; not needed: already all interrupts masked off.
;	mov	al,0FFh		; mask off all interrupts for now
;	out	0A1h,al
;	call	Delay
;	mov	al,0FBh		; mask all irq's but irq2 which
;	out	021h,al			; is cascaded
	ret
EndP	Setup_RM_8259
;; enable A20, reset copro, reprogram interrupts...
;; see Linux: boot/setup.S
EndS	PM_Code
;;;;;;;;;;;;;;;;;;;;;;; Protected to Real Mode switcher ;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_Code
Proc	Go_BIOS_Real
; PREconditions:
; * (!) cs points to a segment such that the PM Data area is at the
;  same offset as it is from the API file (that's klugdy) (FIXME!)
; POSTconditions:
; * Restore Interrupts, I/O devices, and BIOS Data area,
;  so that the BIOS will run properly
; * Do the switching to Real mode
; * jump to address pointed by [RM_Target]
	lidt	[cs:rm_idt_address]	; beware segment restrictions !
EndP	Go_BIOS_Real
Proc	_GoReal
	db	0eah		; far jump to GoReal
	dd	ofs GoReal
	dw	RMPARTCS
EndP	_GoReal
Proc	Ret_From_RM	near
	call	EnableNMI
EndP	Ret_From_RM
EndS	PM_Code
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;; Public declarations ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
Segment	PM_Data
  PMPartELen	= $
EndS	PM_Data
Segment	PM_UData
if1
  PMPartLen	= $
endif
EndS	PM_UData

Public PMPartLen,PMPartELen
Public Go_BIOS_Real,_GoReal
Public __GDT__,__IDT__,__Stack__,__Stack_Top__
Public __PC__,__PD__,__PU__,__PMPART__
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
End
