;
;    exDOS - Experimental 32 Bit protected mode scripted operating system
;    (C) 2005 Jason Hunt
;    
;    This program is free software; you can redistribute it and/or
;    modify it under the terms of the GNU General Public License
;    as published by the Free Software Foundation; either version 2
;    of the License, or (at your option) any later version.
;
;    This program is distributed in the hope that it will be useful,
;    but WITHOUT ANY WARRANTY; without even the implied warranty of
;    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;    GNU General Public License for more details.
;
;    You should have received a copy of the GNU General Public License
;    along with this program; if not, write to the Free Software
;    Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
;
;    Contact: nulluser@gmail.com
;
;
; File: kernel.asm

[ORG 0x10000] 
[CPU 386]
[BITS 16]     

; Some definitions

%define 	REAL_SEG	0x1000		; Segment in real mode
%define		CODE_SEG	0x08		; Code segment in PM
%define		DATA_SEG	0x10		; Data segment in PM
%define		STACK_TOP	0xffff		; Stack in PM
%define 	end_line  	0x0d, 0x0a, 0


; Execution starts here
start:	 
	jmp	REAL_SEG:boot_code	; Force code segment

;  /--------------------\
;  |Start of 16 bit code|
;  \--------------------/
;

boot_code:
	cli				; Disable interrups
	cld				; Clear direction flag

	mov	ax, REAL_SEG		; We know the bios loads into here	
	mov	es, ax 			; Setup real mode segments
	mov	ds, ax			; 
	mov	gs, ax			; 
	mov	fs, ax			; 
					; stack is at 0000:ffff
					
	call	setup_intr		; Setup the PIC and disable interrupts

	mov	si, startmsg1		; Display start message
	call	real_put_string

;	mov 	si, a20msg		; Display A20 message
	;call	real_put_string

	call	enable_a20		; Enable the A20 gate

;	mov	si, startmsg2		; Display protected mode message
;	call	real_put_string

        lgdt	[gdt]        		; Load the GDT puesdodescriptor
			
	smsw    ax             		; set bit 1 of machine staus word
        or      al,1            	; to enable protected mode
        lmsw    ax 


	jmp	dword CODE_SEG: boot_code_32
					; Jump to code segmet
					; dword forces really far call
;
; 16 Bit functions 
;


;  /----------------------------\
;  |Enable and test the A20 gate|
;  \----------------------------/
;

; Enables and tests the A20 gate
enable_a20:
	call	wait_in		
	mov	al, 0xAD	; Disable keyboard
	out	0x64,al

	call	wait_in
	mov	al, 0xD0	; Read command
	out	0x64, al

	call	wait_out
	in	al, 0x60	; Get value
	push	eax

	call	wait_in
	mov	al, 0xD1	; Write command
	out	0x64, al		

	call	wait_in
	pop	eax
	or	al, 0x12
	out	0x60, al	; Set A20 and interrut

	call	wait_in		
	mov	al,0xAE		; Enable keyboard
	out	0x64,al

        call	wait_in

	ret


; Wait for keyboad input buffer empty
wait_in:
	in      al, 0x64
	test    al,2
	jnz	wait_in
	ret

;Wait for keyboard input buffer empty
wait_out:
	in	al, 0x64
	test	al, 1
	jz	wait_out
	ret




;  /-----------------------------------\
;  |Setup the 8259 Interrupt Controller|
;  \-----------------------------------/
;
; Port address for the 8259 Programible interrupt controller

intr_base_1	equ	0x20		; First interrupt controller
intr_base_2	equ	0xA0		; Second interrupt controller
intr_data_1	equ	intr_base_1 + 1 ; First interrupt controller
intr_data_2	equ	intr_base_2 + 1 ; Second interrupt controller
intr_init	equ	0x11		; Cascade mode, ICW4 needed


setup_intr:
	mov	al, intr_init		; Initilize with ICW4 needed
	out	intr_base_1, al		; Send to both controllers
	call	wait_states
	
	out	intr_base_2, al		; ICW1	
	call	wait_states

	mov	al, 0x20		; Setup vector address
	out	intr_data_1,al		; ICW2  0x20
	call	wait_states
	
	mov	al, 0x28		; 
	out	intr_data_2,al		; ICW2   0x28
	call	wait_states

	mov	al, 0x04		; Slave is on S2
	out	intr_data_1, al		; ICW3
	call	wait_states

	mov	al, 0x02		; Slave ID 2
	out	intr_data_2, al		; ICW3
	call	wait_states

	mov	al, 0x01		; 8086 mode
	out	intr_data_1, al		; ICW4
	call	wait_states

	out	intr_data_2, al		; ICW4
	call	wait_states

	mov	al, 0xFF		; Mask all interrupts
	out	intr_data_1, al		; ICW4
	call	wait_states

	out	intr_data_2, al		; ICW4
	call	wait_states

	ret


; Make sure that the 8259 gets enough wait states
wait_states:
	nop	
	nop
	ret



;  /-----------------------\
;  |Real more print string |
;  \-----------------------/
;

; Print string at [ds:si]
real_put_string:
	lodsb
	cmp	al, 0
	jz	.done			; Found null
	mov	ah, 0x0e		; Write char funciton
	int 	0x10			; Call bios
	jmp	real_put_string		; Next char
.done
	ret

; Strings used by 16 bit code

startmsg1	db	"(Stage-1) Script-OS Kernel starting.", 0x0d, 0x0a, 0
;startmsg2	db 	"Entering protected mode.", 0x0d, 0x0a, 0
a20error	db	"Unable to set A20 line. Stop.", 0x0d, 0x0a, 0
;a20msg		db	"Enabling A20 line.", 0x0d, 0x0a, 0

;  /--------------------\
;  | End of 16 bit code|
;  \--------------------/
;

[BITS 32]		


boot_code_32:

	mov	ax, DATA_SEG	; Setup our protected segments
	mov	ds, ax		
	mov	es, ax
	mov	gs, ax
	mov	fs, ax

	mov	ss, ax		; Setup stack 0x10:0xffff, inc downward
	mov	esp, STACK_TOP

	pushf			; Clear nested task flag
	and	dword[esp], 0xffffbfff  ; Do I need to ?
	popf

	call	CODE_SEG:0x10400	; jump to entry
	
	jmp	$			; Main should never return



;;; Setup gloable descriptor table
	
; For code segment
; Access = 0x9A
;   Persent  = 1		; Descriptor slot active
;   priv1    = 0		; Ring 0
;   priv0    = 0 		; Ring 0
;   (unused) = 1		; Must be 1
;   exec     = 1		; Not Executable
;   conform  = 0		; non conforming
;   wri/read = 1		; write enable
;   accessed = 0		; used by cpu
; = 0x9a

%define code_access 0x9a  ;   9e


; For datasegment
; Access = 0x92
;   Persent  = 1		; Descriptor slot active
;   priv1    = 0		; Ring 0
;   priv0    = 0 		; Ring 0
;   (unused) = 1		; Must be 1
;   exec     = 0		; Not Executable
;   conform  = 0		; non conforming
;   wri/read = 1		; read enable
;   accessed = 0		; used by cpu
; = 0x92

%define data_access 0x92

; Flag byte
; granularity  = 1		; 4K granularity 
; default size = 1		; 32 bit access
; (unused)     = 0
; (unused)     = 0 
;
; = 0xcf
; (low 4 bits are limit 19:16)

%define	code_flag 0xcf
%define	data_flag 0xcf


gdt:	dw	gdt_end - gdt - 1	; Size of table	
	dd	gdt			; Store gdt pointer in the 		
	dw	0			; Null descriptor			
	
code_seg:				; Descriptor for code segment
	dd	0x0000ffff	
	db	0
	db	code_access
	db	code_flag
	db	0

data_seg:
	dd	0x0000ffff		; Destriptor for data segment
	db	0
	db	data_access
	db	data_flag
	db	0
gdt_end:	



times 1024-($-$$) db 0	; Pad to 1024 bytes.  kload32 is called at 0x10400




