asm {/* See ::/Doc/Boot.DD. ZealOS starts in real, calls some BIOS routines, switches to 32 bit, and 64 bit mode and continues in ZealC at KMain(). The boot loader jumps here in real-mode (16-bit). It actually jumps to the CZXE header which is placed just before this by the compiler. 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. Kernel.PRJ */ USE16 SYS_KERNEL:: //This must match CKernel. JMP I16 CORE0_16BIT_INIT //************************************ // ASM Global variables required for 16-bit start-up ALIGN 4, OC_NOP SYS_BOOT_SRC:: DU32 BOOT_SRC_NULL; SYS_BOOT_BLK:: DU32 0; SYS_BOOT_PATCH_TABLE_BASE:: DU32 0; SYS_RUN_LEVEL:: DU32 0; #exe { StreamPrint("SYS_COMPILE_TIME:: DU64 0x%X;", Now); //See AHCIBootDVDProbeAll }; #assert SYS_COMPILE_TIME + sizeof(CDate) + sizeof(CZXE) < DVD_BLK_SIZE MEM_BOOT_BASE:: DU32 0; //Offset from start used by reboot MEM_E801:: DU16 0, 0; MEM_E820:: DU8 MEM_E820_ENTRIES_NUM * sizeof(CMemE820) DUP (0); MEM_PHYSICAL_SPACE:: DU64 0; SYS_GDT_PTR:: DU16 sizeof(CGDT) - 1; DU64 0; SYS_PCI_BUSES:: DU16 0; ALIGN 16, OC_NOP SYS_GDT:: //See CGDT GDT_NULL: DU64 0,0; GDT_BOOT_DS: DU64 0x00CF92000000FFFF, 0; //Gets patched. GDT_BOOT_CS: DU64 0x00CF9A000000FFFF, 0; //Gets patched. GDT_CS32: DU64 0x00CF9A000000FFFF, 0; GDT_CS64: DU64 0x00209A0000000000, 0; //The Charter says just ring0. GDT_CS64_RING3: DU64 0x0020FA0000000000, 0; //Ring3, so you can play with. GDT_DS: DU64 0x00CF92000000FFFF, 0; GDT_DS_RING3: DU64 0x00CFF2000000FFFF, 0; GDT_TR: DU8 MP_PROCESSORS_NUM * 16 DUP(0); GDT_TR_RING3: DU8 MP_PROCESSORS_NUM * 16 DUP(0); #assert $ - SYS_GDT == sizeof(CGDT) SYS_FRAMEBUFFER_ADDR:: DU64 0; SYS_FRAMEBUFFER_WIDTH:: DU64 0; SYS_FRAMEBUFFER_HEIGHT:: DU64 0; SYS_FRAMEBUFFER_PITCH:: DU64 0; SYS_FRAMEBUFFER_BPP:: DU8 0; SYS_SMBIOS_ENTRY:: DU64 0; SYS_DISK_UUID:: DU64 0, 0; SYS_BOOT_STACK:: DU32 BOOT_RAM_LIMIT; SYS_IS_UEFI_BOOTED:: DU8 0; SYS_FRAMEBUFFER_LIST:: DU8 sizeof(CVideoInfo) * VBE_MODES_NUM DUP(0); #assert $ - SYS_KERNEL == sizeof(CKernel) - sizeof(CZXE) REQUESTED_SCREEN_WIDTH: DU16 1024; REQUESTED_SCREEN_HEIGHT: DU16 768; VBE_TEMP_MODE: DU8 sizeof(CVBEMode) DUP(0); VBE_INFO: DU8 sizeof(CVBEInfo) DUP(0); VBE_FINAL_MODE_NUM: DU16 0; // gets set to final result mode number VBE_STD_MODE_NUM: DU16 0; // gets set to mode number for standard 1024x768 //************************************ CORE0_16BIT_INIT:: //EAX is SYS_BOOT_SRC. (Value passed from boot block, BootHD, BootDVD, & BootRAM.) MOV ECX, EAX MOV AX, (BOOT_RAM_LIMIT - BOOT_STACK_SIZE) / 16 MOV SS, AX MOV SP, BOOT_STACK_SIZE PUSH ECX //Will be SYS_BOOT_SRC. See BootHD, BootDVD & BootRAM. PUSH EBX 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 @@05 RETF @@05: STI MOV AX, CS MOV DS, AX MOV U32 [SYS_RUN_LEVEL], RLF_16BIT //Our variables are on the data segment, but VBE functions require ES. //moving DS into ES, while preserving ES PUSH ES PUSH DS POP ES //Get VBE implementation information MOV AX, 0x4F00 MOV DI, VBE_INFO MOV CVBEInfo.signature[DI], 'VBE2' //set to 'VBE2' to use VBE 2.0 functionality INT 0x10 POP ES CMP AX, 0x004F JE @@10 JMP $ //Freeze system if VBE not supported @@10: /*Loop variables: DI <- Temporary storage for mode information CX <- Mode number DX <- 'mode' array GS <- Segment for video modes list SI <- Offset for video modes list */ //Obtain segment:offset of list of potential video modes MOV AX, VBE_INFO MOV SI, CVBEInfo.video_modes[AX] MOV GS, CVBEInfo.video_modes+2[AX] MOV DX, SYS_FRAMEBUFFER_LIST MOV DI, VBE_TEMP_MODE @@15: //Loop through all the mode numbers MOV AX, GS:[SI] CMP AX, 0xFFFF //FFFF signifies the end of the list JE @@25 ADD SI, 2 //Increment pointer to read next U16 mode MOV CX, AX BTS CX, 14 //Set linear framebuffer bit in the mode number we are about to pass to the BIOS below PUSH ES PUSH DS POP ES //Get mode information for mode number MOV AX, 0x4F01 INT 0x10 POP ES CMP AX, 0x004F JNE @@15 //if call to get mode information failed //Filter everything but 32-bit color MOV AL, CVBEMode.bpp[DI] CMP AL, 32 JNE @@15 //Check if the mode is actually supported MOV AX, CVBEMode.attributes[DI] AND AX, 0x91 //bit 0 = supported, bit 4 = graphics mode, bit 7 = linear framebuffer CMP AX, 0x91 JNE @@15 //Only want memory model of packed pixel or direct color (RGB) MOV AX, CVBEMode.memory_model[DI] CMP AX, 4 JE @@18 CMP AX, 6 JNE @@15 //Copy information about this mode into AX and BX @@18: MOV BX, CVBEMode.height[DI] @@20: MOV AX, CVBEMode.width[DI] // Copy info into DX SYS_FRAMEBUFFER_LIST MOV CVideoInfo.height[DX], BX MOV CVideoInfo.width[DX], AX ADD DX, sizeof(CVideoInfo) // next array element //Check if width and height match standard 1024x768 //If they match, set VBE_STD_MODE_NUM to this mode num CMP AX, 1024 JNE @@23 CMP BX, 768 JNE @@23 MOV [VBE_STD_MODE_NUM], CX //Check if width and height match requested resolution @@23: CMP AX, [REQUESTED_SCREEN_WIDTH] JNE @@15 CMP BX, [REQUESTED_SCREEN_HEIGHT] JNE @@15 //If we've made it here we have our mode MOV [VBE_FINAL_MODE_NUM], CX JMP @@15 @@25: //End of loop //If requested resolution wasn't found in VBE mode list, //use the standard 1024x768 mode as fallback MOV AX, [VBE_FINAL_MODE_NUM] CMP AX, 0 JNE @@30 MOV CX, [VBE_STD_MODE_NUM] MOV [VBE_FINAL_MODE_NUM], CX @@30: PUSH ES PUSH DS POP ES //Get mode information for the mode we want MOV DI, VBE_TEMP_MODE MOV CX, [VBE_FINAL_MODE_NUM] MOV AX, 0x4F01 INT 0x10 POP ES CMP AX, 0x004F JNE @@35 //if called failed //Set the mode; call takes mode number in BX MOV AX, 0x4F02 MOV BX, CX INT 0x10 CMP AX, 0x004F JNE @@35 //Give mode information to kernel MOV EAX, CVBEMode.framebuffer[DI] MOV U32 [SYS_FRAMEBUFFER_ADDR], EAX MOV AX, CVBEMode.height[DI] MOV U16 [SYS_FRAMEBUFFER_HEIGHT], AX MOV AX, CVBEMode.width[DI] MOV U16 [SYS_FRAMEBUFFER_WIDTH], AX MOV AX, CVBEMode.pitch[DI] MOV U16 [SYS_FRAMEBUFFER_PITCH], AX MOV AL, CVBEMode.bpp[DI] MOV U8 [SYS_FRAMEBUFFER_BPP], AL BTS U32 [SYS_RUN_LEVEL], RLf_VESA @@35: //Get E801 memory map. //Output: AX = Memory between 1MiB and 16MiB in KiB (max 0x3C00 == 15 MiB) // BX = Memory after 16MiB until first memory hole in 64KiB blocks. XOR CX, CX XOR DX, DX MOV AX, 0xE801 INT 0x15 JCXZ @@40 //if CX and DX are zero, use AX and BX instead. MOV AX, CX MOV BX, DX @@40: MOV U16 [MEM_E801], AX MOV U16 [MEM_E801+2], BX //Get E820 memory map. MOV CX, MEM_E820_ENTRIES_NUM - 1 //Leave one to terminate XOR EBX, EBX PUSH DS POP ES MOV DI, MEM_E820 @@45: PUSH CX MOV EAX, 0xE820 MOV ECX, sizeof(CMemE820) MOV EDX, 'PAMS' INT 0x15 JC @@50 CMP EAX, 'PAMS' JNE @@50 TEST EBX, EBX JZ @@50 ADD DI, sizeof(CMemE820) POP CX LOOP @@45 SUB SP, 2 @@50: ADD SP, 2 //if called failed we want to nullify the PUSHed CX value. //Find how much space to map, start with E801 limit. XOR EAX, EAX MOV AX, [MEM_E801+2] SHL EAX, 16 ADD EAX, SYS_16MEG_AREA_LIMIT XOR EDX, EDX //Find max of E820 to set mapped space. MOV SI, MEM_E820 @@55: MOV CL, CMemE820.type[SI] TEST CL, CL JZ @@65 MOV EBX, CMemE820.base [SI] MOV ECX, CMemE820.base+4[SI] ADD EBX, CMemE820.len [SI] ADC ECX, CMemE820.len+4 [SI] SUB EBX, EAX SBB ECX, EDX JC @@60 MOV EAX, CMemE820.base [SI] MOV EDX, CMemE820.base+4[SI] ADD EAX, CMemE820.len [SI] ADC EDX, CMemE820.len+4 [SI] @@60: ADD SI, sizeof(CMemE820) JMP @@55 @@65: MOV [MEM_PHYSICAL_SPACE], EAX MOV [MEM_PHYSICAL_SPACE+4], EDX //Get PCI Bus Info MOV U16 [SYS_PCI_BUSES], 256 XOR DX, DX MOV AX, 0xB101 INT 0x1A CMP DX, 'PC' JNE @@70 MOV CH, 0 INC CX MOV U16 [SYS_PCI_BUSES], CX @@70: CLI //Enable A20 IN AL, 0x92 OR AL, 2 OUT 0x92, AL POP U32 [SYS_BOOT_BLK] POP U32 [SYS_BOOT_SRC] //See BootHD, BootDVD, & BootRAM. CLD XOR EAX, EAX MOV AX, CS MOV DS, AX MOV ES, AX SHL EAX, 4 MOV U32 [MEM_BOOT_BASE], EAX MOV DX, CS SUB DX, sizeof(CZXE) / 16 #assert !(sizeof(CZXE) & 0xF) MOV GS, DX MOV EDX, EAX ADD EDX, U32 GS:[CZXE.patch_table_offset] SUB EDX, sizeof(CZXE) MOV U32 [SYS_BOOT_PATCH_TABLE_BASE], EDX ADD U32 [GDT_BOOT_DS+2], EAX ADD U32 [GDT_BOOT_CS+2], EAX ADD EAX, I32 SYS_GDT MOV U32 [SYS_GDT_PTR + CSysLimitBase.base], EAX LGDT U32 [SYS_GDT_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 to fully implement in the assembler. 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:CORE0_32BIT_INIT DU32 CORE0_32BIT_INIT; DU16 CGDT.boot_cs; #assert $ + 16 <= 0xFFFF }