asm {/*
See $LK,"::/Doc/Boot.TXT"$ for how booting works.
This file calls some BIOS routines before
switching to 32 bit, and then, 64 bit modes.
It continues here: $LK,"Kernel",A="FF:::/Kernel/KEnd.CPP,U0 Kernel"$.

The boot loader jumps here, in real-mode (16-bit).
It actually jumps to the $LK,"CBinFile",A="MN:CBinFile"$ which is placed
just before this $LK,"generated by the compiler",A="FF:::/Compiler/CmpMain.CPP,16 ALIGN"$.
The header begins with a short jmp to
the start of this file's code which begins
with the following small jump past some data.

This file is first in the Kernel image
because it is #included first.$LK,"Kernel.PRJ",A="FF:::/Kernel/Kernel.PRJ,KStart:1"$
*/
USE16
SYS_KERNEL_START1:: //This must match $LK,"CKernel",A="MN:CKernel"$.
	JMP	U16 SYS_KERNEL_START2

//************************************
//  ASM Global vars required for 16-bit start-up
	ALIGN	4,OC_NOP
SYS_BOOT_BASE::			DU32	0;	//Fixed offset from start used by reboot
SYS_BOOT_PATCH_TABLE_BASE::	DU32	0;
#exe {StreamPrint("SYS_COMPILE_TIME:: DU64 0x%X;",Now);}
//See $LK,"BootCdProbe",A="MN:BootCdProbe"$
#assert SYS_COMPILE_TIME+8+sizeof(CBinFile)<CD_BLK_SIZE

SYS_OS_32START::	DU32	SYS_INIT_OS;
SYS_BOOT_CODE::		DU32	0xFF;
SYS_MEM_E801::		DU16	0,0;
SYS_MEM_E820::		DU8	MEM_NUM_E820_ENTRIES*MEM_E820_ENTRY_SIZE DUP (0);

	ALIGN	16,OC_NOP
SYS_GDT:: //See $LK,"CGDT",A="MN:CGDT"$
GDT_NULL:	DU64	0,0;
GDT_BOOT_DS:	DU64	0,0; //Gets filled-in
GDT_BOOT_CS:	DU64	0,0; //Gets filled-in
GDT_CS32:	DU64	0x00CF9A000000FFFF,0;
GDT_CS64:	DU64	0x00209A0000000000,0; //The $LK,"Charter",A="FI:::/Doc/Charter.TXT"$ says just ring0.
GDT_CS64_RING3:	DU64	0x0020FA0000000000,0; //$LK,"Ring3",A="FI:::/Demo/Lectures/Ring3.CPP"$, in case you want to play around.
GDT_DS:		DU64	0x00CF92000000FFFF,0;
GDT_DS_RING3:	DU64	0x00CFF2000000FFFF,0;
GDT_TR:		DU8	MP_MAX_PROCESSORS*16 DUP(0);
GDT_TR_RING3:	DU8	MP_MAX_PROCESSORS*16 DUP(0);
#assert $$-SYS_GDT==sizeof(CGDT)

SYS_BIOS_GR_MODE::	DU16	0;
SYS_PCI_BUSSES::	DU16	0;
#assert $$-SYS_KERNEL_START1==sizeof(CKernel)-sizeof(CBinFile)

SYS_RUN_LEVEL::		DU32	0;
SYS_TEMP_PTR::		DU16	0;
			DU64	0;
//************************************
SYS_KERNEL_START2::
//EBX is SYS_BOOT_CODE (val passed from boot blk)
	MOV	AX,(BOOT_RAM_LIMIT-BOOT_STK_SIZE)/16
	MOV	SS,AX
	MOV	SP,BOOT_STK_SIZE
	PUSH	EBX		//will be SYS_BOOT_CODE
	MOV	AX,VGA_TEXT_MEM_BASE/16
	MOV	ES,AX
	MOV	U32 ES:[0],'O'+BLUE<<12+('S'+GREEN<<12)<<16

	CALL	U16 GET_IP
GET_IP:	POP	BX
	SUB	BX,GET_IP
	SHR	BX,4
	MOV	AX,CS
	ADD	AX,BX
	PUSH	AX
	PUSH	U16 TEMPLEOS_CONT_START
	RETF

TEMPLEOS_CONT_START:
	STI
	MOV	AX,CS
	MOV	DS,AX

	MOV	U32 [SYS_RUN_LEVEL],RLF_16BIT

	MOV	BX,0x12		//640x480 16 color
	MOV	U16 [SYS_BIOS_GR_MODE],BX
	MOV	AX,0x4F02
#exe {
  if (!kernel_cfg->opts[CFG_TEXT_MODE])
    StreamPrint("INT 0x10");	//Enable VGA
};
	CMP	AX,0x004F
	JE	@@10
	MOV	U16 [SYS_BIOS_GR_MODE],0 //failed
@@10:

//Get mem maps
	MOV	AX,0xE801
	INT	0x15
	MOV	U16 [SYS_MEM_E801],CX
	MOV	U16 [SYS_MEM_E801+2],DX

	MOV	CX,MEM_NUM_E820_ENTRIES-1 //Leave one to terminate
	XOR	EBX,EBX
	MOV	AX,DS
	MOV	ES,AX
	MOV	DI,SYS_MEM_E820
@@15:	PUSH	CX
	MOV	EAX,0xE820
	MOV	ECX,MEM_E820_ENTRY_SIZE
	MOV	EDX,'PAMS'
	INT	0x15
	JC	@@20
	CMP	EAX,'PAMS'
	JNE	@@20
	TEST	EBX,EBX
	JZ	@@20
	ADD	DI,MEM_E820_ENTRY_SIZE
	POP	CX
	LOOP	@@15
	JMP	@@25
@@20:	ADD	SP,2

//Get PCI Bus Info
@@25:	MOV	U16 [SYS_PCI_BUSSES],256
	XOR	DX,DX
	MOV	AX,0xB101
	INT	0x1A
	CMP	DX,'PC'
	JNE	@@30
	MOV	CH,0
	INC	CX
	MOV	U16 [SYS_PCI_BUSSES],CX
@@30:

	CLI
//Enable A20
	IN	AL,0x92
	OR	AL,2
	OUT	0x92,AL

	CLD			//Start with known regs
	MOV	AX,CS
	MOV	DS,AX
	MOV	ES,AX
	MOV	FS,AX
	MOV	GS,AX
	XOR	EAX,EAX
	XOR	EBX,EBX
	XOR	ECX,ECX
	XOR	EDX,EDX
	XOR	ESI,ESI
	XOR	EDI,EDI
	XOR	EBP,EBP

	XOR	EAX,EAX
	MOV	AX,CS
	SHL	EAX,4
	MOV	U32 [SYS_BOOT_BASE],EAX

	POP	U32 [SYS_BOOT_CODE]

	MOV	BX,CS
	SUB	BX,sizeof(CBinFile)/16
#assert !(sizeof(CBinFile)&15)
	MOV	GS,BX
	MOV	EBX,U32 GS:[CBinFile.patch_table_offset]
	ADD	EAX,EBX
	SUB	EAX,sizeof(CBinFile)
	MOV	U32 [SYS_BOOT_PATCH_TABLE_BASE],EAX

	MOV	U32 [GDT_BOOT_DS],0x0000FFFF
	MOV	U32 [GDT_BOOT_DS+4],0x0CF9200
	MOV	U32 [GDT_BOOT_CS],0x0000FFFF
	MOV	U32 [GDT_BOOT_CS+4],0x0CF9A00
	MOV	EAX,U32 [SYS_BOOT_BASE]
	ADD	U32 [GDT_BOOT_DS+2],EAX
	ADD	U32 [GDT_BOOT_CS+2],EAX

	//Set-up ptr to limit and base of GDT.
	MOV	U16 [SYS_TEMP_PTR],sizeof(CGDT)-1
	MOV	EAX,U32 [SYS_BOOT_BASE]
	ADD	EAX,SYS_GDT
	MOV	U32 [SYS_TEMP_PTR+2],EAX
	MOV	U16 [SYS_TEMP_PTR+6],0

	LGDT	U32 [SYS_TEMP_PTR]
	MOV	EAX,SYS_START_CR0
	MOV_CR0_EAX

/*
The assembler doesn't support far jumps so we hand code it.
16-bit code is not important enough for me to implement
16-bit mode in the assembler fully.

To complete the switch to 32-bit mode, we have to load
the code segment with a far jump.
*/
	DU8	0x66,0xEA;		 //JMP CGDT.boot_cs:SYS_INIT_OS
	DU32	SYS_INIT_OS;
	DU16	CGDT.boot_cs;

#assert $$+16<=0xFFFF
USE32
//************************************
//  ASM Global vars not required for 16-bit start-up
	ALIGN	8,OC_NOP
SYS_HEAP_BASE::		DU64	SYS_FIXED_AREA+sizeof(CSysFixedArea);
SYS_HEAP_LIMIT::	DU64	0;

#exe {
  StreamPrint(
"SYS_MEM_INIT_FLAG::	DU8	%d;"
"SYS_MEM_INIT_VAL::	DU8	%d;"
"SYS_HEAP_INIT_FLAG::	DU8	%d;"
"SYS_HEAP_INIT_VAL::	DU8	%d;"
"SYS_VAR_INIT_FLAG::	DU8	%d;"
"SYS_VAR_INIT_VAL::	DU8	%d;",
kernel_cfg->opts[CFG_MEM_INIT], kernel_cfg->mem_init_val,
kernel_cfg->opts[CFG_HEAP_INIT],kernel_cfg->heap_init_val,
kernel_cfg->opts[CFG_VAR_INIT], kernel_cfg->var_init_val);
};
SYS_HEAP_DBG_FLAG::	DU8	_CFG_HEAP_DBG;
	ALIGN	8,0
SYS_CTRL_ALT_FLAGS::	DU64	0;
SYS_EXTERN_TABLE::	DU64	0;

SYS_CODE_BP::		DU64	0;
SYS_DATA_BP::		DU64	0;

SYS_PROGRESSES::
SYS_PROGRESS1::		DU64	0;
SYS_PROGRESS1_MAX::	DU64	0;
SYS_PROGRESS1_DESC::	DU8	PROGRESS_DESC_LEN+1 DUP(0);
SYS_PROGRESS2::		DU64	0;
SYS_PROGRESS2_MAX::	DU64	0;
SYS_PROGRESS2_DESC::	DU8	PROGRESS_DESC_LEN+1 DUP(0);
SYS_PROGRESS3::		DU64	0;
SYS_PROGRESS3_MAX::	DU64	0;
SYS_PROGRESS3_DESC::	DU8	PROGRESS_DESC_LEN+1 DUP(0);
SYS_PROGRESS4::		DU64	0;
SYS_PROGRESS4_MAX::	DU64	0;
SYS_PROGRESS4_DESC::	DU8	PROGRESS_DESC_LEN+1 DUP(0);
#assert $$-SYS_PROGRESSES==sizeof(CProgress)*4

SYS_FOCUS_TASK::	DU64	0;
SYS_CPU_STRUCTS::	DU64	0;
SYS_MP_CNT::		DU64	1;
SYS_MP_CNT_INITIAL::	DU64	1;
SYS_MP_CNT_LOCK::	DU64	1;

	ALIGN	DFT_CACHE_LINE_WIDTH,OC_NOP
SYS_CACHE_LINE_WIDTH::	DU64	DFT_CACHE_LINE_WIDTH;
	DU8	DFT_CACHE_LINE_WIDTH-sizeof(CBinFile)-8 DUP(0);
SYS_SEMAS::		DU8	NUM_SYS_SEMAS*SEMA_STRUCT_SIZE DUP(0);
//************************************
	ALIGN	16,OC_NOP
SYS_INIT_OS:: //Entry point for RAM reboots.
	PUSH	U32 SYS_START_RFLAGS
	POPFD
	MOV	EAX,SYS_START_CR0
	MOV_CR0_EAX

	MOV	AX,CGDT.boot_ds	//LOAD DS SELECTOR
	MOV	DS,AX
	MOV	ES,AX

	MOV	EAX,U32 [SYS_RUN_LEVEL]
	AND	EAX,RLF_16BIT
	OR	EAX,RLF_32BIT
	MOV	U32 [SYS_RUN_LEVEL],EAX

//Patch abs addresses
	MOV	ESI,U32 [SYS_BOOT_PATCH_TABLE_BASE]
	MOV	EDI,U32 [SYS_BOOT_BASE]
	MOV	AX,CGDT.ds
	MOV	DS,AX
	MOV	ES,AX

	MOV	ECX,U32 CPatchTableAbsAddr.abs_addres_cnt[ESI]
	LEA	ESI,U32 CPatchTableAbsAddr.abs_addres[ESI]
@@05:	LODSD
	ADD	EAX,EDI
	ADD	U32 [EAX],EDI
	LOOP	@@05

	MOV	AX,CGDT.ds
	MOV	DS,AX
	MOV	ES,AX
	MOV	FS,AX
	MOV	GS,AX
	MOV	SS,AX
	MOV	ESP,BOOT_RAM_LIMIT //Temp Stk

	DU8	0xEA; //JMP CGDT.cs32:SYS_CONT_INIT_OS
	DU32	SYS_CONT_INIT_OS;
	DU16	CGDT.cs32;

SYS_CONT_INIT_OS::
	LOCK
	BTS	U32 [SYS_RUN_LEVEL],RLf_PATCHED

	CALL	SYS_FIND_BIOS32_SERVICE_DIR
	CALL	SYS_FIND_PCI_SERVICES

	MOV	U32 [SYS_HEAP_BASE],SYS_FIXED_AREA+sizeof(CSysFixedArea)
	CALL	SYS_INIT_MEM

	PUSH	U32 0
	CALL	INIT_EM64T
USE64
	LOCK
	BTS	U32 [SYS_RUN_LEVEL],RLf_64BIT

	FNINIT
	MOV	RAX,SYS_FIXED_AREA+CSysFixedArea.init_fpu_mmx
	FXSAVE	U64 [RAX]

//Init CPU0 Struct
	PUSH	SYS_FIXED_AREA+CSysFixedArea.adam
	PUSH	SYS_FIXED_AREA+CSysFixedArea.boot_cpu
	PUSH	0
	CALL	&CPUStructInit
	CALL	SET_GS_BASE

//Init Adam heap ctrl
	MOV	EDI,U32 SYS_FIXED_AREA+CSysFixedArea.adam_hc
	MOV	EAX,U32 SYS_FIXED_AREA+CSysFixedArea.adam_bp
	MOV	U64 CHeapCtrl.bp[RDI],RAX
	MOV	U32 CHeapCtrl.hc_signature[RDI],HEAP_CTRL_SIGNATURE_VAL

	
	LEA	RAX,U64 CHeapCtrl.next_um-CMemUsedAllocated.next[RDI]
	MOV	U64 CHeapCtrl.next_um[RDI],RAX
	MOV	U64 CHeapCtrl.last_um[RDI],RAX

	MOV	EAX,U32 SYS_FIXED_AREA+CSysFixedArea.adam
	MOV	U64 CHeapCtrl.mem_task[RDI],RAX
	MOV	U64 CTask.code_heap[RAX],RDI
	MOV	U64 CTask.data_heap[RAX],RDI
	MOV	U32 CTask.task_signature[RAX],TASK_SIGNATURE_VAL
	MOV	U32 CTask.task_in_que_signature[RAX],TASK_IN_QUE_SIGNATURE_VAL
	
	LOCK
	BTS	U32 [SYS_RUN_LEVEL],RLf_BOOT_HEAP

	PUSH	ADAM_STK
	PUSH	U32 SYS_FIXED_AREA+CSysFixedArea.adam
	CALL	&TaskInit
	CALL	SET_FS_BASE
	MOV	RSP,U64 CTask.rsp[RAX]

	JMP	I32 &KernelMain

//************************************
USE32
INIT_EM64T:
	MOV_EAX_CR4
	OR	EAX,0xB0
	MOV_CR4_EAX

	MOV	EAX,SYS_FIXED_AREA+CSysFixedArea.pml4
	MOV_CR3_EAX

	MOV	ECX,IA32_EFER
	XOR	EDX,EDX
	MOV	EAX,IA32F_LME
	WRMSR

	MOV_EAX_CR0
	BTS	EAX,31	//enable paging (required for 64-bit mode)
	MOV_CR0_EAX

	DU8	0xEA;	   //JMP CGDT.cs64:EM64T_PART3
	DU32	EM64T_PART3;
	DU16	CGDT.cs64;

USE64
EM64T_PART3:
	RET

//************************************
SYS_RESTART_KERNEL::
	XOR	RDI,RDI
	MOV	EDI,U32 SYS_BOOT_BASE
	MOV	EDI,U32 [RDI]
	XOR	RSI,RSI
	MOV	ESI,U32 SYS_OS_32START
	MOV	ESI,U32 [RSI]
	ADD	ESI,EDI

	PUSH	U32 CGDT.ds		//stk seg
	PUSH	U32 BOOT_RAM_LIMIT	//stk
	PUSH	U32 0			//flags
	PUSH	U32 CGDT.cs32
	LEA	RAX,[@@10]
	PUSH	RAX
	IRET
USE32
@@10:
	WBINVD
//disable paging
	MOV_EAX_CR0
	BTR	EAX,31
	MOV_CR0_EAX

	MOV	ECX,IA32_EFER
	XOR	EDX,EDX
	XOR	EAX,EAX
	WRMSR

	MOV	EBX,BOOT_CODE_RAM
	JMP	ESI
USE64
SYS_RESTART_KERNEL_END::
}
