; ------------------------------------------------------------------------------
; Discarded file (Released as Public Domain 2012-11-31)
;
; Written by Antti (http://forum.osdev.org)
;
; This is my first file for my first OS. It contains all beginners
; mistatakes, hard-coded values, bad modularity, etc. This can be
; used as an example of bad design.
;
; ------------------------------------------------------------------------------

%ifndef SYSTEM_SIZE_32K
%define SYSTEM_SIZE_32K 224
%endif

%define WELCOME_MESSAGE 'Loading the system...'

[BITS 16]
[ORG 0x7C00]

boot_sector:
	jmp short .start

align 4, db 0
.drive_index: dd 0x00000000

times 0x80 - ($ - $$) db 0
.start:
	cli
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov ax, 0x7C00
	mov sp, ax
	sti
	cld

	and dx, 0xFF
	mov [.drive_index], dx

	mov ax, 0x4100
	mov bx, 0x55AA
	int 0x13
	jc short .error
	cmp bx, 0xAA55
	jne short .error

	mov ax, 0x4800
	mov dx, [.drive_index]
	mov si, .drive_parameters
	int 0x13
	jc short .error

%ifdef BOOT_FROM_CD
	cmp word [.bytes_per_sector], 2048
%else
	cmp word [.bytes_per_sector], 512
%endif
	jne short .error

	mov ax, 0x4200
	mov dx, [.drive_index]
	mov si, .disk_address_packet
	int 0x13
	jc short .error

	jmp 0x0000:second_stage

align 2, db 0
.error:
	hlt
	jmp short .error

%ifdef BOOT_FROM_CD
align 16, db 0
.disk_address_packet:
	db 0x10
	db 0x00
	dw 1
	dw 0x8400, 0x0000
	dq 0x1001
%else
align 16, db 0
.disk_address_packet:
	db 0x10
	db 0x00
	dw (8 - 1)
	dw 0x7E00, 0x0000
	dq 0x01
%endif

align 16, db 0
.drive_parameters:
	dw 0x001E
	dw 0x0000
	dd 0x00
	dd 0x00
	dd 0x00
	dq 0x00
.bytes_per_sector:
	dw 0x00
	dd 0x00

%ifndef BOOT_FROM_CD
times 440 - ($ - $$) db 0
dd 0x00000000 ; Disk Signature
dw 0x0000     ; Unused
db 0x00       ; Non-Bootable
db 0x00       ; Head
db 0x00       ; Sector/Cylinder
db 0x00       ; Cylinder
db 0xEF       ; Partition Type (EFI)
db 0x00       ; Head
db 0x00       ; Sector/Cylinder
db 0x00       ; Cylinder
dd 0x00003C00 ; First Sector (LBA)
dd 0x00000100 ; Number of Sectors
%endif

times 510 - ($ - $$) db 0
dw 0xAA55

; ******************************************************************************

times 2048 - ($ - $$) db 0
second_stage:
	call welcome_message
	call get_memory_map
	call check_memory

	call check_long_mode
	call get_vesa_info

	push word 32
	push word 1024
	push word 1280
	call find_vesa_mode@6
	cmp ax, 0xFFFF
	jne .continue

	push word 32
	push word 768
	push word 1024
	call find_vesa_mode@6
	cmp ax, 0xFFFF
	jne .continue

	push word 32
	push word 600
	push word 800
	call find_vesa_mode@6
	cmp ax, 0xFFFF
	je set_vesa_mode.error
align 2
.continue:
	call set_vesa_mode
	call set_video_information_structure

	call enable_a20
	call disable_interrupts

	xor ax, ax
	mov ds, ax
	lidt [interrupt_descriptor_table]
	lgdt [global_descriptor_table]

	mov eax, cr0
	or eax, 0x01
	mov cr0, eax

	jmp 0x08:protected_mode

; ******************************************************************************

align 16, db 0
system_loading:
.counter        dd SYSTEM_SIZE_32K
.destination:   dd 0x00200000

%ifdef BOOT_FROM_CD
.source_sector: dd 0x00001002
%else
.source_sector: dd 0x00000008
%endif

.boolean:       dd 0x00000000

[BITS 32]

align 16, db 0
protected_mode:
	mov eax, 0x10
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov eax, 0x7C00
	mov esp, eax

	bts dword [system_loading.boolean], 0x00
	jc .continue

	cld
	xor eax, eax
	mov edi, 0x00200000
	mov ecx, (0x00800000 / 4)
	rep stosd

	jmp 0x18:.p_m_16

align 4
.continue:
	cld
	mov esi, 0x10000
	mov edi, [system_loading.destination]
	mov ecx, (0x8000 / 4)
	cmp dword [system_loading.counter], 0x00000001
	jne .skip
	sub ecx, (8 * 512 / 4)
align 4
.skip:
	rep movsd

	add dword [system_loading.destination], 0x8000

%ifdef BOOT_FROM_CD
	add dword [system_loading.source_sector], 16
%else
	add dword [system_loading.source_sector], 64
%endif

	dec dword [system_loading.counter]
	je .end

	jmp 0x18:.p_m_16
align 4
.end:
	mov dword [system_loading.counter],       SYSTEM_SIZE_32K
	mov dword [system_loading.destination],   0x00200000

%ifdef BOOT_FROM_CD
	mov dword [system_loading.source_sector], 0x00001002
%else
	mov dword [system_loading.source_sector], 0x00000008
%endif

	mov dword [system_loading.boolean],       0x00000000

	jmp 0x08:init_long_mode

[BITS 16]

align 4
.p_m_16:
	mov ax, 0x20
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax

	mov eax, cr0
	and eax, 0xFFFFFFFE
	mov cr0, eax

	jmp 0x0000:real_mode

align 4, db 0
real_mode:
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax
	mov ss, ax
	mov ax, 0x7C00
	mov sp, ax

	lidt [interrupt_descriptor_table]

%ifdef BOOT_FROM_CD
	mov ax, 16
	cmp word [system_loading.counter], 0x0001
	jne .continue
	sub ax, 2
%else
	mov ax, 64
	cmp word [system_loading.counter], 0x0001
	jne .continue
	sub ax, 8
%endif

align 2
.continue:
	push word [system_loading.source_sector]
	push ax
	call load_system@4

	xor ax, ax
	mov ds, ax
	lidt [interrupt_descriptor_table]
	lgdt [global_descriptor_table]

	mov eax, cr0
	or eax, 0x01
	mov cr0, eax

	jmp 0x08:protected_mode

; ------------------------------------------------------------------------------
align 2, db 0
write_message:
	push si
align 2
.write_character:
	lodsb
	test al, 0xFF
	je .end
	mov ah, 0x0E
	xor bx, bx
	int 0x10
	jmp .write_character
align 2
.end:
	pop si
	ret
; ------------------------------------------------------------------------------
align 2, db 0
welcome_message:
	mov ax, 0x0F00
	int 0x10

	mov ah, 0x01
	mov cx, 0x2607
	int 0x10

	mov si, .message
	call write_message

	ret

align 2, db 0
.message: db 0x0D, 0x0A, WELCOME_MESSAGE, 0x0D, 0x0A, 0x0D, 0x0A, 0x00
; ------------------------------------------------------------------------------
align 2, db 0
get_memory_map:
	xor ax, ax
	mov es, ax
	mov di, 0x0E00
	call .clear_entry

	add di, 0x20
	call .clear_entry
	add di, 20
	mov dword [es:di], 0x01
	sub di, 20

	mov eax, 0x0000E820
	xor ebx, ebx
	mov ecx, 24
	mov edx, 0x534D4150
	int 0x15
	jc .error
	cmp eax, 0x534D4150
	jne .error
	test bx, bx
	je .error
	jmp .skip1
align 2
.loop:
	call .clear_entry
	mov eax, 0x0000E820
	mov ecx, 24
	mov edx, 0x534D4150
	int 0x15
	jc .end
align 2
.skip1:
	test cx, cx
	je .next
	cmp cx, 20
	jbe .skip2
	test byte [es:di + 20], 1
	je .next
align 2
.skip2:
	mov eax, [es:di + 8]
	mov ecx, [es:di + 12]
	or eax, ecx
	je .next

	inc word [0x0E00]
	add di, 32
align 2
.next:
	test bx, bx
	jne .loop
align 2
.end:
	ret
align 2
.clear_entry:
	push ax
	push cx
	push di
	cld
	xor ax, ax
	mov cx, 0x10
	rep stosw
	pop di
	pop cx
	pop ax
	ret
align 2
.error:
	mov si, .error_message
	call write_message
align 2
.halt_loop:
	hlt
	jmp .halt_loop
align 2, db 0
.error_message: db '[Hardware is not supported]', 0x0D, 0x0A, 0x00
; ------------------------------------------------------------------------------
align 2, db 0
check_memory:
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov dx, ax

	mov bx, 0x0E00
	mov cx, [bx]
	and cx, 0x1FF
	add bx, 0x20
align 2
.loop:
	cmp byte [bx+16], 0x01
	jne .skip

	cmp word [bx+6], 0x00
	jne .skip
	cmp word [bx+4], 0x00
	jne .skip

	cmp word [bx+2], 0x0010
	ja .skip
	jne .skip_1
	cmp word [bx+0], 0x00
	jne .skip
align 2
.skip_1:
	cmp word [bx+14], 0x00
	jne .valid

	cmp word [bx+12], 0x00
	jne .valid

	cmp word [bx+10], 0x00A0
	jae .valid

	jmp .skip
align 2
.valid:
	mov dx, 0x01
	jmp .end
align 2
.skip:
	add bx, 0x20
	dec cx
	jne .loop
align 2
.end:
	test dx, 0x01
	je .error
	ret
align 2
.error:
	mov si, .error_message
	call write_message
align 2
.halt_loop:
	hlt
	jmp .halt_loop
align 2, db 0
.error_message: db '[Memory layout is not compatible]', 0x0D, 0x0A, 0x00
; ------------------------------------------------------------------------------
align 2, db 0
check_long_mode:
	mov eax, 0x80000000
	cpuid
	cmp eax, 0x80000000
	jbe .error
	mov eax, 0x80000001
	cpuid
	test edx, 0x20000000 ; 64-bit
	je .error
	test edx, 0x00100000 ; NX-bit
	je .error
	test edx, 0x00000800 ; SYSCALL/SYSRET
	je .error
	ret
align 2
.error:
	mov si, .error_message
	call write_message
align 2
.loop:
	hlt
	jmp .loop
align 2, db 0
.error_message: db '[CPU is not compatible]', 0x0D, 0x0A, 0x00
; ------------------------------------------------------------------------------
align 2, db 0
get_vesa_info:
	cld
	xor ax, ax
	mov es, ax
	mov di, 0x0600 ; VESA Info Buffer (1024 bytes)
	push di
	mov cx, 0x200
	rep stosw
	pop di

	mov byte [es:di + 0], 'V'
	mov byte [es:di + 1], 'B'
	mov byte [es:di + 2], 'E'
	mov byte [es:di + 3], '2'

	mov ax, 0x4F00
	int 0x10
	cmp ax, 0x004F
	jne .error

	cmp byte [0x0600 + 0], 'V'
	jne .error
	cmp byte [0x0600 + 1], 'E'
	jne .error
	cmp byte [0x0600 + 2], 'S'
	jne .error
	cmp byte [0x0600 + 3], 'A'
	jne .error

	cmp word [0x0600+0x04], 0x0200 ; VESA 2.0
	jb .error

	mov bx, [0x0600 + 0x0E + 0x02]
	mov es, bx
	mov bx, [0x0600 + 0x0E]

	mov dx, bx
	and dx, 0x0F
	shr bx, 4
	mov ax, es
	add ax, bx
	jo .error
	mov es, ax
	mov bx, dx

	mov ax, 0x1000
	mov ds, ax
	xor ax, ax
	mov di, ax
align 2
.copy_modes:
	mov ax, [es:bx]
	mov [di], ax
	add bx, 2
	add di, 2
	cmp ax, 0xFFFF
	jne short .copy_modes
	xor ax, ax
	mov ds, ax
	mov es, ax
	mov di, ax
	mov bx, ax

	ret
align 2
.error:
	mov si, .error_message
	call write_message
align 2
.loop:
	hlt
	jmp .loop
align 2, db 0
.error_message: db '[VBE2 is not available]', 0x0D, 0x0A, 0x00
; ------------------------------------------------------------------------------
align 2, db 0
find_vesa_mode@6:
	push bp
	mov bx, 0x1000
	mov es, bx
	xor bx, bx
align 2
.loop:
	mov ax, [es:bx]
	cmp ax, 0xFFFF
	je .end
	push es
	cld
	xor ax, ax
	mov es, ax
	mov di, 0x0A00 ; VESA Mode Buffer (1024 bytes)
	push di
	mov cx, 0x200
	rep stosw
	pop di
	pop es

	mov cx, [es:bx]

	push es
	push bx
	push cx
	xor ax, ax
	mov es, ax
	mov ax, 0x4F01
	int 0x10
	pop cx
	pop bx
	pop es

	cmp ax, 0x004F
	jne .next

	mov ax, [0x0A00]

	test ax, 0x01 ; Mode supported?
	je .next

	test ax, 0x08 ; Color?
	je .next

	test ax, 0x10 ; Graphics?
	je .next

	test ax, 0x80 ; Linear Framebuffer supported?
	je .next

	cmp byte [0x0A00 + 0x1B], 0x06 ; Direct Color?
	jne .next

	mov bp, sp
	mov ax, [bp+0x04] ; Screen Width
	cmp [0x0A00 + 0x12], ax
	jne .next

	mov ax, [bp+0x06] ; Screen Height
	cmp [0x0A00 + 0x14], ax
	jne .next

	mov ax, [bp+0x08] ; Bits Per Pixel
	cmp [0x0A00 + 0x19], al
	jne .next

	mov ax, 0x0008
	cmp [0x0A00 + 0x1F], al ; Red Mask Size
	jne .next

	cmp [0x0A00 + 0x21], al ; Green Mask Size
	jne .next

	cmp [0x0A00 + 0x23], al ; Blue Mask Size
	jne .next

	mov ax, [0x0A00 + 0x28 + 0x02]
	test ax, 0xFF00
	je .next

	mov ax, cx
	jmp .end
align 2
.next:
	add bx, 2
	jmp .loop
align 2
.end:
	xor bx, bx
	mov es, bx
	pop bp
	ret 6
; ------------------------------------------------------------------------------
align 2, db 0
set_vesa_mode:
	mov bx, ax
	and bx, 0x01FF
	or bx, 0x4000 ; Enable Linear Framebuffer

	mov ax, 0x4F02
	mov di, 0x0800 ; Unnecessary
	int 0x10

	cmp ax, 0x004F
	jne .error

	ret
align 2
.error:
	mov si, .error_message
	call write_message
align 2
.loop:
	hlt
	jmp .loop
align 2, db 0
.error_message: db '[Video hardware is not supported]', 0x0D, 0x0A, 0x00
; ------------------------------------------------------------------------------
align 2, db 0
set_video_information_structure:
	cld
	xor ax, ax
	mov cx, (8 * 8 / 2)
	mov es, ax
	mov di, 0x0800
	rep stosw

	mov ax, [0x0A00+0x28+0x00]
	mov [0x0800+0x00], ax
	mov ax, [0x0A00+0x28+0x02]
	mov [0x0800+0x02], ax

	mov ax, [0x0A00+0x10]
	mov [0x0800+0x08], ax

	cmp word [0x0600+0x04], 0x0300 ; VESA 3.0
	jb .continue
	mov ax, [0x0A00+0x32]
	mov [0x0800+0x08], ax
align 2
.continue:
	mov ax, [0x0A00+0x12]
	mov [0x0800+0x10], ax

	mov ax, [0x0A00+0x14]
	mov [0x0800+0x18], ax

	xor ax, ax
	mov al, [0x0A00+0x19]
	mov [0x0800+0x20], al

	mov al, [0x0A00+0x20]
	mov [0x0800+0x28], al

	mov al, [0x0A00+0x22]
	mov [0x0800+0x30], al

	mov al, [0x0A00+0x24]
	mov [0x0800+0x38], al

	ret

; Video information structure (0x00800)
; 0x00 QWORD  Pointer to Video Buffer
; 0x08 QWORD  Bytes Per Scan Line (800*3 or greater)
; 0x10 QWORD  Width (800 or greater)
; 0x18 QWORD  Height (600 or greater)
; 0x20 QWORD  Bits Per Pixel (24 or 32)
; 0x28 QWORD  Red Field Position
; 0x30 QWORD  Green Field Position
; 0x38 QWORD  Blue Field Position
; ------------------------------------------------------------------------------
align 2, db 0
enable_a20:
	push es
	call .test_a20
	test ax, ax
	jne .end

	mov ax, 0x2401
	int 0x15
	jnc .test_loop

	xor ax, ax
	in al, 0x92
	test ax, 0x0002
	jne .test_loop

	or ax, 0x0002
	and ax, 0x00FE
	out 0x92, al

align 2
.test_loop:
	call .test_a20
	test ax, ax
	je .test_loop
align 2
.end:
	mov word [0x7DFE], 0xAA55
	pop es
	ret

align 2
.test_a20:
	xor ax, ax
	mov es, ax
	mov bx, 0x7DFE
	mov cx, [es:bx]

	not ax
	mov es, ax
	mov bx, 0x7E0E
	mov dx, [es:bx]

	cmp cx, dx
	jne .test_a20_enabled

	inc cx
	xor ax, ax
	mov es, ax
	mov bx, 0x7DFE
	mov [es:bx], cx

	not ax
	mov es, ax
	mov bx, 0x7E0E
	mov dx, [es:bx]

	cmp cx, dx
	jne .test_a20_enabled

	xor ax, ax
	ret
align 2
.test_a20_enabled:
	ret
; ------------------------------------------------------------------------------
align 2, db 0
disable_interrupts:
	cli

	; Disable NMI
	in al, 0x70
	or al, 0x80
	out 0x70, al

	; Disable PIC
	mov ax, 0x00FF
	out 0xA1, al
	out 0x21, al

	mov cx, 0xFFFF
align 2
.loop:
	nop
	nop
	dec cx
	jne .loop
	ret
; ------------------------------------------------------------------------------
align 2, db 0
load_system@4:
	push bp
	mov bp, sp

	mov ax, [bp+0x04]
	mov [.disk_address_packet_size], ax

	mov ax, [bp+0x06]
	mov [.disk_address_packet_from], ax

	clc
	mov eax, 0x4200
	mov ebx, 0x00
	mov ecx, 0x00
	mov edx, [boot_sector.drive_index]
	mov esi, 0x00
	mov si, .disk_address_packet
	mov edi, 0x00
	int 0x13
	jc .error

	mov word [.disk_address_packet_size], 0x0000
	mov word [.disk_address_packet_from], 0x0000

	pop bp
	ret 4

align 2
.error:
	hlt
	jmp .error

align 16, db 0
.disk_address_packet:
                           db 0x10
                           db 0x00
.disk_address_packet_size: dw 0x00
                           dw 0x0000, 0x1000
.disk_address_packet_from: dq 0x00

; ******************************************************************************

align 4, db 0
dw 0x0000
global_descriptor_table:
	dw (7*8-1)
	dw global_descriptor_table_entries, 0x0000

align 16, db 0
global_descriptor_table_entries:
	dd 0x00000000 ; Null 0x00
	dd 0x00000000

	dd 0x0000FFFF ; Code Segment 0x08
	dd 0x00CF9A00

	dd 0x0000FFFF ; Data Segment 0x10
	dd 0x00CF9200

	dd 0x0000FFFF ; Real Mode Code Segment 0x18
	dd 0x00009A00

	dd 0x0000FFFF ; Real Mode Data Segment 0x20
	dd 0x00009200

	dd 0x0000FFFF ; Long Mode Code Segment 0x28
	dd 0x00AF9A00

	dd 0x0000FFFF ; Long Mode Data Segment 0x30
	dd 0x00AF9200

align 4, db 0
dw 0x0000
interrupt_descriptor_table:
	dw (256*8-1)
	dd 0x00000000

; ******************************************************************************

align 16, db 0
[BITS 32]
init_long_mode:
	cld
	xor eax, eax
	mov edi, 0x10000
	mov ecx, (0x70000 / 4)
	rep stosd

.PML4E:
	mov dword [0x00020000], (0x00021000 + 0x1B)

	mov eax, 0x00021000
	mov edx, (0x00022000 + 0x1B)
	mov ecx, 8
align 2
.PDPE:
	mov [eax], edx
	add eax, 8
	add edx, 0x1000
	dec ecx
	jne .PDPE

	mov eax, 0x00022000
	mov edx, 0x9B
	mov ecx, (512*8)
align 2
.PDE:
	mov [eax], edx
	or dword [eax+4], 0x80000000 ; NX-bit
	add eax, 8
	add edx, 0x00200000
	dec ecx
	jne .PDE

	xor dword [0x00022004], 0x80000000 ; Zero NX-bit (First page)
	xor dword [0x0002200C], 0x80000000 ; Zero NX-bit (Second page)

	jmp 0x18:.p_m_16

align 4, db 0
[BITS 16]
.p_m_16:
	mov eax, cr4
	bts eax, 5
	mov cr4, eax

	mov eax, 0x00020018
	mov cr3, eax

	mov ecx, 0xC0000080
	rdmsr
	bts eax, 8
	bts eax, 11
	wrmsr

	mov eax, cr0
	bts eax, 31
	mov cr0, eax

	db 0x66
	db 0xEA
	dw long_mode, 0x0000
	dw 0x28

; ******************************************************************************

align 8, db 0
[BITS 64]
long_mode:
	mov eax, 0x30
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	lidt [long_interrupt_descriptor_table]

	jmp end_of_boot

align 16, db 0
dd 0x00000000
dw 0x0000
long_interrupt_descriptor_table:
	dw (256*16-1)
	dq 0x10000

; ******************************************************************************

align 4, db 0
end_of_boot:
	cld
	xor rax, rax
	xor rbx, rbx
	xor rcx, rcx
	xor rdx, rdx

	mov rsp, 0x10000
	push rax
	push rbx
	push rcx
	push rdx
	mov rbp, rsp

	mov rdi, 0x00414C4148494954 ; Magic Value (TIIHALA)
	mov esi, 0x0800             ; Video Information
	mov edx, 0x0E00             ; Memory Map, Count
	mov ecx, 0x0E20             ; Memory Map, Array

	push qword 0x0000000000200000
	ret

; ******************************************************************************

times 4096 - ($ - $$) db 0
