;
; Pmode setup stub
; (A20 enable code and PIC reprogram from linux bootsector)
;

;
; Base address of the kernel
;
KERNEL_BASE      equ     0c0000000h

;
; Segment selectors
;
USER_CS       equ     08h
USER_DS       equ     010h
KERNEL_CS     equ     020h
KERNEL_DS     equ     028h
                                     
;
; Space reserved in the gdt for tss descriptors
;
NR_TASKS        equ     128

;
; We are a .com program
;
org 100h

;
; 16 bit code
;
BITS 16

entry:
        ;
        ; Load stack
        ;
        cli
        push    ds
        pop     ss
        mov     sp,real_stack_end
        sti

        ;
        ; Setup the loader space
        ;
        mov     ebx,0
        mov     eax,0
        mov     ecx,0
        mov     edx,0
        mov     esi,0
        mov     edi,0

        ;
        ; Calculate the end of this module
        ;
        mov     ax,ds
        movzx   ebx,ax
        shl     ebx,4
        add     ebx,_end

        ;
        ; Round up to the nearest page
        ;
        and     ebx,~0xfff
        add     ebx,01000h

        ;
        ; Set the start of the page directory
        ;
        mov     [kernel_page_directory_base],ebx

        ;
        ; Set the start of the continuous range of physical memory
        ; occupied by the kernel
        ;
        mov     [_start_mem],ebx
        add     ebx,01000h

        ;
        ; Calculate the start of the system page table (0xc0000000 upwards)
        ;
        mov     [system_page_table_base],ebx
        add     ebx,01000h

        ;
        ; Calculate the start of the page table to map the first 4mb
        ;
        mov     [lowmem_page_table_base],ebx
        add     ebx,01000h

        ;
        ; Set the position for the first module to be loaded
        ;
        mov     [next_load_base],ebx

        ;
        ; Set the address of the start of kernel code
        ;
        mov     [_start_kernel],ebx

        ;
        ; Make the argument list into a c string
        ;
        mov     di,081h
l1:
        cmp     byte [di],0dh
        je      l2
        cmp     byte [di],' '
        jne      l12
        mov     byte [di],0
l12:
        inc     di
        jmp     l1
l2:
        mov     byte [di],0
        mov     [end_cmd_line],di

        mov     dx,082h
l14:
        mov     bx,dx
        cmp     byte [bx],0
        je      l16

        ;
        ; Process the arguments
        ;
        mov     di,loading_msg
        call    _print_string
        mov     di,dx
        call    _print_string
        mov     ah,02h
        mov     dl,0dh
        int     021h
        mov     ah,02h
        mov     dl,0ah
        int     021h

        ;
        ; Load the file
        ;
        push    di
        mov     dx,di
        call    _load_file
        pop     di

        ;
        ; Move onto the next module name in the command line
        ;
l15:
        cmp     di,[end_cmd_line]
        je      l16
        cmp     byte [di],0
        je      l17
        inc     di
        jmp     l15
l17:
        inc     di
        mov     dx,di
        jmp     l14
l16:

        ;
        ; Set the end of kernel memory 
        ;
        mov     eax,[next_load_base]
        mov     [_end_mem],eax

        ;
        ; Begin the pmode initalization
        ;
        jmp     _to_pmode

exit:
        mov     ax,04c00h
        int     21h

        ;
        ; Any errors detected jump here
        ;
_error:
        mov     di,err_msg
        call    _print_string
        jmp     exit

end_cmd_line dw 0


;
; Read in a file to kernel_base, set kernel base to the end of the file in
; memory rounded up to the nearest page
;
; In:
;       DI = filename
;
_load_file:
        inc     dword [_nr_files]

        ;
        ; Open the file
        ;
        mov     ah,03dh
        mov     al,0
        mov     dx,di
        int     021h
        jc      _error
        mov     [file_handle],ax
        
        ;
        ; Find filelength
        ;
        mov     ah,042h
        mov     al,2
        mov     bx,[file_handle]
        mov     cx,0
        mov     dx,0
        int     021h

        ;
        ; Convert the filelength in DX:AX to a dword in EBX
        ;
        movzx   ebx,dx
        shl     ebx,16
        mov     bx,ax

        ;
        ; Record the length of the module in boot parameter table
        ;
        mov     esi,[_nr_files]
        dec     esi
        mov     [_module_lengths+esi*4],ebx

        ; 
        ; Convert the length into 
        ;
        mov     [size_mod_4k],bx
        and     word [size_mod_4k],0fffh

        shr     ebx,12
        mov     [size_div_4k],ebx


        ;
        ; Seek to beginning of file
        ;
        mov     ah,042h
        mov     al,0
        mov     bx,[file_handle]
        mov     cx,0
        mov     dx,0
        int     021h
        jc      _error

        ;
        ;  Read in the module
        ;
        push    ds

        ;
        ; Convert the linear point to the load address into a seg:off
        ;
        mov     edi,[next_load_base]
        call    convert_to_seg
        mov     dx,di

        ;
        ; Move onto the next position in prepartion for a future read
        ;
        mov     eax,[size_div_4k]
        mov     bx,[size_mod_4k]
        cmp     bx,0
        je      l20
        inc     eax        
l20:
        shl     eax,0ch
        add     [next_load_base],eax

        push    fs
        pop     ds
       
        ;
        ; We read the kernel in 4k chunks (because)
        ;
l6:
        ;
        ; Check if we have read it all
        ;
        mov     ax,[es:size_div_4k]
        cmp     ax,0
        je      l5

        ;
        ; Make the call (dx was loaded above)
        ;
        mov     ah,3fh
        mov     bx,[es:file_handle]
        mov     cx,01000h
        int     21h               

        ;
        ; We move onto the next pointer by altering ds
        ;
        mov     ax,ds
        add     ax,0100h
        mov     ds,ax
        dec     word [es:size_div_4k]
        jmp     l6

l5:
        ;
        ; Read the last section
        ;
        mov     ah,3fh
        mov     bx,[es:file_handle]
        mov     cx,[es:size_mod_4k]
        int     21h
        pop     ds
        jnc     _no_error
        jmp     _error

_no_error:
        ret

;
; In: EDI = address
; Out: FS = segment
;      DI = base
;
convert_to_seg:
        push    eax

        mov     eax,edi
        shr     eax,16
        shl     eax,12
        mov     fs,ax

        and     edi,0ffffh

        pop     eax
        ret

;
; Print string in DS:DI
;
_print_string:
        push    ax
        push    dx
        push    di
        mov     ah,02h
l3:
        mov     dl,[di]
        cmp     dl,0
        je      l4
        int     021h
        inc     di
        jmp     l3
l4:
        pop     di
        pop     dx
        pop     ax
        ret

;
; Handle of the currently open file
;
file_handle dw 0

;
; Size of the current file mod 4k
;
size_mod_4k dw 0

;
; Size of the current file divided by 4k
;
size_div_4k dd 0

;
;
;
last_addr dw 0

;
; Generic error message
;
err_msg db 'Error during operation',0
rostitle db '',0

;
;
;
loading_msg db 'Loading: ',0
death_msg: db 'death', 0

filelength_lo dw 0
filelength_hi dw 0

kernel_page_directory_base dd 0
system_page_table_base dd 0
lowmem_page_table_base dd 0
next_load_base dd 0
_start_kernel dd 0

boot_param_struct_base dd 0

;
; These variables are passed to the kernel (as a structure)
;
align 4
_boot_param_struct:
_magic:
        dd 0
_cursorx:
        dd 0
_cursory:
        dd 0
_nr_files:
        dd 0
_start_mem:
        dd 0
_end_mem:
        dd 0
_module_lengths:
        times 64 dd 0
_end_boot_param_struct
             
;
; Needed for enabling the a20 address line
;
empty_8042:
        jmp     $+3
        jmp     $+3
        in      al,064h
        test    al,02h
        jnz     empty_8042
	ret

;
; GDT descriptor
;
align 8
gdt_descr:
gdt_limit:
        dw (((6+NR_TASKS)*8)-1)
gdt_base:
        dd gdt


_to_pmode:
        ;
        ; Setup kernel parameters
        ;
        mov     dword [_magic],0xdeadbeef

        ;
        ; Save cursor position
        ;
        mov     ax,3          ;! Reset video mode
        int     10h


        mov     bl,10
        mov     ah,12
        int     10h
        
        mov     ax,1112h      ;! Use 8x8 font
	xor	bl,bl
        int     10h
        mov     ax,1200h      ;! Use alternate print screen
        mov     bl,20h
        int     10h
        mov     ah,1h         ;! Define cursor (scan lines 6 to 7)
        mov     cx,0607h
        int     10h

        mov     ah,1
        mov     cx,0600h
        int     10h

        MOV       AH,6        ;SCROLL ACTIVE PAGE UP
        MOV       AL,32H     ;CLEAR 25 LINES
        MOV       CX,0H       ;UPPER LEFT OF SCROLL
        MOV       DX,314FH    ;LOWER RIGHT OF SCROLL
        MOV       BH,1*10h+1       ;USE NORMAL ATTRIBUTE ON BLANKED LINE
        INT       10H         ;VIDEO-IO


        mov     dx,0
        mov     dh,0

        mov     ah,02h
        mov     bh,0
        int     10h

        mov     dx,0
        mov     dh,0

        mov     ah,02h
        mov     bh,0
        int     010h
          
        mov     ah,03h
        mov     bh,0h
        int     010h
        movzx   eax,dl
        mov     [_cursorx],eax
        movzx   eax,dh
        mov     [_cursory],eax

        mov     bx,ds
        movzx   eax,bx
        shl     eax,4
        add     eax,_boot_param_struct
        mov     [boot_param_struct_base],eax        

        cli

        ;
        ; Zero out the kernel page directory
        ;
        ;
        mov     edi,[kernel_page_directory_base]
        call    convert_to_seg

        mov     cx,1024
l10:
        mov     dword [fs:di],0
        add     di,4
        loop    l10

        ;
        ; Map in the lowmem page table (and reuse it for the identity map)
        ;
        mov     edi,[kernel_page_directory_base]
        call    convert_to_seg

        mov     eax,[lowmem_page_table_base]
        add     eax,07h
        mov     [fs:di],eax
        mov     [fs:di+(0xd0000000/(1024*1024))],eax
	
	;
	; Map the page tables from the page table
	;
	mov     eax,[kernel_page_directory_base]
	add     eax,07h
	mov     [fs:di+(0xf0000000/(1024*1024))],eax
	
        ;
        ; Map in the kernel page table
        ;
        mov     eax,[system_page_table_base]
        add     eax,07h
        mov     [fs:di+3072],eax

        ;
        ; Setup the lowmem page table
        ;
        mov     edi,[lowmem_page_table_base]
        call    convert_to_seg

        mov     ebx,0
l9:
        mov     eax,ebx
        shl     eax,12        ; ebx = ebx * 4096
        add     eax,07h       ; user, rw, present
        mov     [fs:edi+ebx*4],eax
        inc     ebx
        cmp     ebx,1024
        jl      l9

        ;
        ; Setup the system page table
        ;
        mov     edi,[system_page_table_base]
        call    convert_to_seg

        mov     eax,07h
l8:
        mov     edx,eax
        add     edx,[_start_kernel]
        mov     [fs:edi],edx
        add     edi,4
        add     eax,1000h
        cmp     eax,100007h
        jl      l8
        
        ;
        ; Load the page directory into cr3
        ;
        mov     eax,[kernel_page_directory_base]
        mov     cr3,eax

        ;
        ; Setup various variables
        ;
        mov     bx,ds
        movzx   eax,bx
        shl     eax,4
        add     [gdt_base],eax

        ;
        ; Enable the A20 address line (to allow access to over 1mb)
        ;
	call	empty_8042
        mov     al,0D1h                ; command write
        out     064h,al
	call	empty_8042
        mov     al,0DFh                ; A20 on
        out     060h,al
	call	empty_8042

        ;
        ; Reprogram the PIC because they overlap the Intel defined
        ; exceptions 
        ;
        mov     al,011h                ; initialization sequence
        out     020h,al                ; send it to 8259A-1
        dw   0x00eb,0x00eb           ; jmp $+2, jmp $+2
        out     0A0h,al                ; and to 8259A-2
        dw   0x00eb,0x00eb
        mov     al,040h                ; start of hardware int's (0x20)
        out     021h,al
        dw   0x00eb,0x00eb
        mov     al,048h                ; start of hardware int's 2 (0x28)
        out     0A1h,al
        dw   0x00eb,0x00eb
        mov     al,04h                ; 8259-1 is master
        out     021h,al
        dw  0x00eb,0x00eb
        mov     al,002h                ; 8259-2 is slave
        out     0A1h,al
        dw   0x00eb,0x00eb
        mov     al,01h                ; 8086 mode for both
        out     021h,al
        dw  0x00eb,0x00eb
        out     0A1h,al
        dw   0x00eb,0x00eb
        mov     al,0FFh                ; mask off all interrupts for now
        out     021h,al
        dw   0x00eb,0x00eb
        out     0A1h,al

        ;
        ; Load stack
        ;
        mov     bx,ds
        movzx   eax,bx
        shl     eax,4
        add     eax,real_stack_end
        mov     [real_stack_base],eax
        mov     esp,[real_stack_base]
        mov     edx,[boot_param_struct_base]

        ;
        ; load gdt
        ;
        lgdt    [gdt_descr]

        ;       
        ; Enter pmode and clear prefetch queue
        ;
        mov     eax,cr0
        or      eax,0x80000001
        mov     cr0,eax
        jmp     next
next:
        ;
        ; NOTE: This must be position independant (no references to
        ; non absolute variables)
        ;

        ;
        ; Initalize segment registers
        ;
        mov     ax,KERNEL_DS
        mov     ds,ax
        mov     ss,ax        
        mov     es,ax
        mov     fs,ax
        mov     gs,ax

        ;
        ; Initalize eflags
        ;
        push    dword 0
        popf

        ;
        ; Jump to start of 32 bit code at c0000000
        ;
        push    edx
        push    dword 0
        jmp     dword KERNEL_CS:(KERNEL_BASE+0x1000)


;
; Our initial stack
;
real_stack times 1024 db 0
real_stack_end:

real_stack_base dd 0


;
; Global descriptor table
;
align 8
gdt:
        dw 0               ; Zero descriptor
        dw 0
        dw 0
        dw 0
                                
        dw 00000h          ; User code descriptor
        dw 00000h          ; base: 0h limit: 3gb
        dw 0fa00h
        dw 000cch
                               
        dw 00000h          ; User data descriptor
        dw 00000h          ; base: 0h limit: 3gb
        dw 0f200h
        dw 000cch
                            
        dw 00000h          
        dw 00000h         
        dw 00000h
        dw 00000h

        dw 0ffffh          ; Kernel code descriptor 
        dw 00000h          ; 
        dw 09a00h          ; base 0h limit 4gb
        dw 000cfh
                               
        dw 0ffffh          ; Kernel data descriptor
        dw 00000h          ; 
        dw 09200h          ; base 0h limit 4gb
        dw 000cfh

                                
        times NR_TASKS*8 db 0

_end:



