; $Id: bootsec.asm,v 1.6 2002/04/25 21:00:41 reddawg Exp $

%include "gdtnasm.inc"

[ORG 0x7c00]
jmp start
nop

id                               db    'UbixFS' ;file system id
version                      dd   1h          ; Filing System Version
fs_start                     dd   101          ; LBA address for start of root dir
krnl_size                   dd   59            ; Kernel size in sectors, starts at sector 1
BytesPerSector         dw  512
SectorsPerTrack        dw   18
TotalHeads               dw   2
TotalSectors              dd   2880     ; 1474560/512 for a 1.44meg disk
file_entry_nextdata  equ  273      ; Offset in file_entry structure to the nextdata LBA pointer
data_entry_data       equ  9          ; Offset in data_entry structure to the data
bootdrv                      db    0

start:
xor ax, ax
mov ds, ax
mov [bootdrv], dl
;mov al,0x13
;int 0x10
; First get into protected mode
cli
n5:
  in al, 0x64  ;Enable A20 {4A} {5}
  test al, 2
  jnz	n5
  mov	al, 0xD1
  out	0x64, al
n6:
  in al, 0x64
  test al, 2
  jnz n6
  mov al, 0xDF
  out 0x60, al
  lgdt    [gdtinfo]               ;Load GDT
  mov ecx, CR0		;Switch to protected mode
  inc cx
  mov CR0, ecx
  mov	ax, flat_data-gdt_table	; Selector for 4Gb data seg
  mov	ds, ax			; {2} Extend limit for ds
  mov	es, ax			; Extend limit for es
  mov	fs, ax			; fs and...
  mov 	gs, ax			; gs
  dec cx				; switch back to real mode
  mov CR0, ecx
  sti
  xor ax, ax
  mov ds, ax
  mov dl, [bootdrv]
  mov bx, 0x60
  mov es, bx
  mov eax, 1
  mov ecx, [krnl_size]
  mov di, 1
load_loop:
  call read_sectors
  inc eax
  mov bx, es
  add bx, 32
  mov es, bx
  loop load_loop
  ; Turn off the floppy motor, its annoying leaving it on !
  mov edx,0x3f2
  mov al,0x0c
  out dx,al
  ;lets convert the ELF file to a linear binary so we can execute it
  cmp dword [0x600],464c457fh  ; The ELF signature is \07fELF
  jne ldr_ELF_err            ; Ugh... not an ELF file !!!
  cmp word [0x600+4],101h    ; It should be statically linked etc.
  jne ldr_ELF_err
  cmp byte [0x600+6],1
  jne ldr_ELF_err
  jmp short skip_err_handler
  ldr_ELF_err:
  mov ax, 'E'+0x0E00
  mov bx, 7
  int 10h
  mov al, 'L'
  int 10h
  mov al, 'F'
  int 10h
  cli
  hlt
skip_err_handler:

	mov eax, [0x600+18h]
	mov [krnl_entry], eax

        xor ecx,ecx                     ; Get the number of sections in cx
        mov cx,[0x600+2ch]

sectionloop:
        dec cx                          ; Next section
        push cx                         ; Save cx on the stack while we load
                                        ; the section into memory

        mov eax,[0x600+2ah]        ; Get the program header entry size
        mul cx                          ; Calculate the offset from the start
                                       ; of the program header table
        mov ebx,[0x600+1ch]        ; Get the PHT offset in ebx
        add ebx,eax                     ; Add it to our PHT entry offset
        add ebx,0x600              ; Calculate the address of the entry

        cmp dword [bx],1                ; Does this section have to be
                                        ; loaded into memory ?
        jne nextsect                    ; No, next section

        mov dword ecx,[bx+4h]           ; Get the offset of the segment in
                                        ; the ELF file

        mov dword ebp,[bx+10h]          ; Get the size of the segment in the
                                        ; ELF file

        mov dword edi,[bx+8h]           ; Get the memory address of the sect.
        mov dword eax,[bx+14h]          ; Get the size of the section in
        mov ebx,eax                     ; the memory into ebx

; ds:dx  = Address of ASCIIZ filename
; es:edi = Where in memory to put it
; ecx    = Offset in file to start reading (bytes)
; ebp    = Length of segment to read (bytes)
;
; Returns:
; eax    = Length of file that was loaded
; eax    = 0 if an error occured


	push ebp
	pusha
	mov esi, 0x600
	add esi, ecx
	mov ecx, ebp
	call memcopy
	popa
	pop eax

        sub ebx,eax                     ; This amount needs to be zeroed
        jz nextsect                     ; It's ok, next section

        add edi,eax                     ; Zero the memory from this address
        xor ax,ax                       ; edi is an absolute address
        mov ecx,ebx
        call zero_memblock              ; Zero the rest of the section

nextsect:
        pop cx                          ; Restore our section count
        or cx,cx                        ; Was this the last one ?
	
	jnz sectionloop


; Re-enter protected mode ! A20 is already enabled
	cli		; No interrupts please at all
	lgdt [gdtinfo]
	mov ecx, cr0
	inc cx
	mov cr0, ecx
	mov ax, flat_data-gdt_table
	mov ds, ax
	mov es, ax
	mov fs, ax
	mov gs, ax

	jmp dword (flat_code-gdt_table):pmode1

	pmode1:
[BITS 32]

	push dword 2
	popfd

	mov eax, [krnl_entry]

	call eax

	cli
	hlt

;Hang the system..
hang:	jmp hang

[BITS 16]
	read_sectors:
; Input:
;	EAX = LBN
;	DI  = sector count
;	ES = segment
; Output:
;	BL = low byte of ES
;	EBX high half cleared
;	DL = 0x80
;	EDX high half cleared
;	ESI = 0

	pusha

	cdq			;edx = 0
	movzx	ebx, byte [SectorsPerTrack]
	div	ebx		;EAX=track ;EDX=sector-1
	mov	cx, dx		;CL=sector-1 ;CH=0
	inc	cx		;CL=Sector number
	xor	dx, dx
	mov	bl, [TotalHeads]
	div	ebx

	mov	dh, dl		;Head
	mov	dl, [bootdrv]	;Drive 0
	xchg	ch, al		;CH=Low 8 bits of cylinder number; AL=0
	shr	ax, 2		;AL[6:7]=High two bits of cylinder; AH=0
	or	cl, al		;CX = Cylinder and sector
	mov	ax, di		;AX = Maximum sectors to xfer
retry:	mov	ah, 2		;Read
	xor	bx, bx
	int	13h
	jc retry

	popa

	ret

; zero_memblock:  Fills the specified memory block with zeros (0x0)
;
; Takes parameters:
; ax    = segment/selector of memory to be cleared
; edi   = offset of memory to be cleared
; ecx   = number of bytes to clear
;
; Returns:
; nothing

zero_memblock:
        push eax                ; Save the registers
        push edi
        push ecx
        push es
        mov es,ax
        xor eax,eax             ; Fill the memory with zeros (0x0)
        cld                     ; Clear the direction flag; rep increments di
        a32 rep stosb           ; Fill the memory (one byte at a time)
        pop es                  ; Restore the registers
        pop ecx
        pop edi
        pop eax
        ret                     ; Return to the main program

; Parameters
; DS:ESI = Source
; DS:EDI = Destination
; CX = length
memcopy:
	pusha
memcopy_loop:
	mov al, [esi]
	mov [edi], al
	inc edi
	inc esi
	loop memcopy_loop
	popa
	ret

print:
  lodsb                                       ; load next character
  or      al, al                              ; test for NUL character
  jz      .DONE
  mov     ah, 0x0E                            ; BIOS teletype
  mov     bh, 0x00                            ; display page 0
  mov     bl, 0x07                            ; text attribute
  int     0x10                                ; invoke BIOS
  jmp     print
  .DONE:
    ret

gdtinfo:

dw	gdtlength
dd	gdt_table

;********* GDT TABLE
gdt_table:

null_desc	desc	0,0,0

flat_code	desc	0, 0xFFFFF, D_CODE + D_READ + D_BIG + D_BIG_LIM

flat_data	desc	0, 0xFFFFF, D_DATA + D_WRITE + D_BIG + D_BIG_LIM

gdtlength equ $ - gdt_table - 1
;********* END GDT TABLE
krnl_entry	dd	0

bootMsg                   db    'Loading UbixOS' , 0x0D, 0x0A, 0x00

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