        .386p

include hybernat.inc
include hyberhd.inc
include int.inc
include doshlp.inc

OUT32BIT	equ	1
HYBERLDR	equ	1
include ..\..\bios\out.inc

PAGESHIFT	equ	12

RTCCTL		equ	070h
RTCDTA		equ	071h
TIMER_0		equ	040h
TIMER_1		equ	041h
TIMER_2		equ	042h
TIMERCB		equ	043h			; timer control byte

DELAY	macro
	local	dly

	push	ecx
	; arbitrary value - just to let registers settle a bit
	mov	ecx, 0800h
dly:
	loop	dly
	pop	ecx
	endm

_TEXT   segment use32 para 'CODE'
        assume cs:FLAT,ds:FLAT,es:FLAT

iostr	db	'io retry: rc = ', 0

; puts message out on debuger port
;	eax = optional parameter to add to end of output message
;	edx = comm port address
;	ecx = nothing - used for scratch
public msgOut
msgOut	proc        near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

	pop	ecx				; grab return addr
	push	ecx
	push	esi
	shr	ecx, PAGESHIFT
	shl	ecx, PAGESHIFT
	add	ecx, offset iostr
	mov	esi, ecx
	out_w_str
	out_dword
	out_crlf
	pop	esi
        ret
msgOut	endp

; do inb instr. - i/o addr is passed in eax
public inb
inb	proc        near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

        push	edx
	mov	edx, eax
	in	al, dx
	pop	edx
        ret
inb	endp

; do outb instr. - i/o addr is passed in edx; value in eax
public outb
outb	proc        near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

	out	dx, al
        ret
outb	endp

; check to see if its ready - return when free with status reg in eax
; note: this uses alternate status register - no interrupt acknowledge
public	hdWaitReady
hdWaitReady	proc	near

	push	edx
	xor	eax, eax
	mov	dx, HD_ASR			; alternate status reg
notready:
	in	al, dx
	test	al, HD_BSY
	jnz	notready
	test	al, HD_DRDY
	jz	notready
itsready:
	pop	edx
	ret
hdWaitReady	endp

; wait till the busy bit is reset - return with status reg in eax
public hdWaitNotBusy
hdWaitNotBusy	proc	near

	push	edx
	xor	eax, eax
	mov	dx, HD_C0 + HD_SR
stillbsy:
	in	al, dx
	test	al, HD_BSY
	jnz	stillbsy
	pop	edx
	ret
hdWaitNotBusy	endp

; read from disk -
;	EAX - mydata pointer
;	EDX - buffer addr
;	ECX - length
; 
public	hdReadDisk
hdReadDisk	proc	near

	push	ebx
	push	edi
	push	esi
	mov	esi, eax			; mydata pointer
	mov	edi, edx			; addr
	xor	ebx, ebx			; count
	mov	dx, HD_C0 + HD_DR
chkfordata:
	call	hdWaitReady			; uses asr
	test	al, HD_DRQ
	jz	chkfordata
again:
	in	ax, dx
	mov	[edi + ebx], ax
	add	ebx, 2
	test	ebx, 1ffh			; end of sector?
	jnz	again				; no - no need to check for end
	dec	[esi].sectorCount
	jnz	skip
	mov	eax, [esi].maxblk
	mov	[esi].sectorCount, eax
	call	hdWaitNotBusy			; clears interrupt
	test	al, HD_ERR
	jnz	readerr
skip:
	cmp	ecx, ebx			; done?
	jne	chkfordata
	xor	eax, eax

readerr:
	DELAY					; TBR - maxtor drvs work better
	pop	esi
	pop	edi
	pop	ebx
	ret
hdReadDisk	endp

; restart the kernel
;	eax = pointer to hyberInfoHdr
public restart
restart	proc	near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

	; point edi to hyberInfoHdr
	mov	edi, eax

	; set idt
 	lidt	fword ptr [edi].hih_idt

	; set cr3
	mov	eax, [edi].hih_cr3
	mov	cr3, eax

	; enable paging
	mov	eax, [edi].hih_cr0
	mov	cr0, eax
	jmp	qflsh2
qflsh2:

	; start setting various registers to kernel values
	mov	ebx, [edi].hih_flags
	push	ebx
	popfd						; interrupts disabled

	; cr4 if necessary
	test	[edi].hih_hyberflags, HF_LOADCR4
	jz	noCR4
	mov	ebx, [edi].hih_cr4
;	mov	cr4, ebx
	db	00fh
	db	022h
	db	0e3h
noCR4:

	; pic masks
	mov	al, [edi].hih_pic1mask
	out	PIC1_PORT1, al
	DELAY
	mov	al, [edi].hih_pic2mask
	out	PIC2_PORT1, al

	; rtc status
	mov	al, 0ah				; status reg a
	out	RTCCTL, al
	DELAY
	mov	al, [edi].hih_rtca
	out	RTCDTA, al
	DELAY
	mov	al, 0bh				; status reg b
	out	RTCCTL, al
	DELAY
	mov	al, [edi].hih_rtcb
	out	RTCDTA, al
	DELAY
	mov	al, 0ch
	out	RTCCTL, al
	DELAY
	in	al, RTCDTA
	DELAY

	; comm state - com1
	mov	dx, COM1_PORT + COM_LCR		; line control reg
	mov	al, 80h
	out	dx, al				; set DLAB
	lea	eax, [edi].hih_com1
	mov	eax, [eax]			; fetch first word
	mov	dx, COM1_PORT + COM_DLM		; divisor latch
	out	dx, al
	shr	eax, 8
	mov	dx, COM1_PORT + COM_DLL		; divisor latch
	out	dx, al
	mov	al, [edi].hih_com1.hc_lcr
	mov	dx, COM1_PORT + COM_LCR		; line control
	out	dx, al
	shr	eax, 8
	mov	dx, COM1_PORT + COM_MCR		; modem control
	out	dx, al
	shr	eax, 8
	mov	dx, COM1_PORT + COM_IEN		; interrupt enable
	out	dx, al

	; comm state - com2
	mov	dx, COM2_PORT + COM_LCR		; line control reg
	mov	al, 80h
	out	dx, al				; set DLAB
	lea	eax, [edi].hih_com2
	mov	eax, [eax]			; fetch first word
	mov	dx, COM2_PORT + COM_DLM		; divisor latch
	out	dx, al
	shr	eax, 8
	mov	dx, COM2_PORT + COM_DLL		; divisor latch
	out	dx, al
	mov	al, [edi].hih_com2.hc_lcr
	mov	dx, COM2_PORT + COM_LCR		; line control
	out	dx, al
	shr	eax, 8
	mov	dx, COM2_PORT + COM_MCR		; modem control
	out	dx, al
	shr	eax, 8
	mov	dx, COM2_PORT + COM_IEN		; interrupt enable
	out	dx, al
	
	; we need to mark the kernel tss not busy
	xor	eax, eax
	mov	ax, [edi].hih_GDTFrame
	shl	eax, PAGESHIFT				; gdt phys addr
	add	ax, [edi].hih_tr
	mov	ebx, [eax+4]				; 2nd dword of tss desc
	and	ebx, not 200h				; busy bit
	mov	[eax+4], ebx
	mov	ax, [edi].hih_tr
	ltr	ax

	; misc regs
	mov	ax, [edi].hih_gs
	mov	gs, ax
	mov	ax, [edi].hih_fs
	mov	fs, ax
	lgdt	fword ptr [edi].hih_gdt			; edi=InfoHdr phys addr
	mov	ax, [edi].hih_ldt
	lldt	ax

	mov	ebp, [edi].hih_ebp
	mov	bx, [edi].hih_ss
	mov	ss, bx
	mov	esp, [edi].hih_esp

	; restore the dpl in the ds selector
	xor	ebx, ebx
	mov	bx, ds
	add	ebx, [edi].hih_gdt2			; address of gdt
	; TBR - save original in InfoHdr
	mov	eax, [ebx+4]				; fetch ds descriptor
	or	eax, 00006000h				; set dpl to 0
	mov	[ebx+4], eax

;	int	3

	mov	eax, [edi].hih_reentry
	jmp	eax

restart	endp

; drive the cpu off the edge causing a reboot
public reboot
reboot	proc        near
        assume ds:FLAT, es:FLAT, fs:nothing, gs:nothing, ss:nothing

	xor	ax, ax
        smsw	ax
reboot	endp

_TEXT   ends

        end

