
	      BOOT_SEG	=  0x7c0
	     VIDEO_SEG	=  0xb87c
	    KERNEL_SEG	=  256

		   BDA	=   64
		   MEM	=   19		; # of 1k pages

		 VIDEO	=   16		; BIOS video interrupt
	   WRITE_CHARS	=    9

		 COLOR	=    3
		YELLOW	=   14
	    SOLID_LINE	=  254
	       DISK_IO	=   19
	     DISK_READ	=    2
	      KEYBOARD	=   22
	   SET_CUR_POS	=    2
		   TTY	=   14
	     READ_SECT	=    2
	   PACKET_SIZE	=   72

	  Get_D_Params	=  0x48

; ============================================================================================

    ; This is peculular to my version of boot loader as I want to display intial contents of
    ; registers immediately after BIOS has passed to this loader.

	pusha				; AX, CX, DX, BX, SP, BP, SI, DI.

	push	gs
	push	fs

	push	ss
	push	es
	push	ds
	push	cs

	mov	bx, ss			; Pointer must be saved because stack is about
	mov	si, sp			; to be changed.

	jmp	BOOT_SEG:Begin		; Most BIOS's have CS @ 0.

		align	8
; --------------------------------------------------------------------------------------------

  ShowS:
	mov	ah, TTY 		; Teletype output
	xor	cx, cx

   @@:	lodsb				; Get ASCIIZ character
	or	al, al
	jz	@F

	int	VIDEO
	inc	cx
	jmp	@B

   @@:	ret				; Terminator reached.

	align	16
; --------------------------------------------------------------------------------------------

    ; Calculate and set a new stack pointer to upper most part of conventional memory,
    ; just below EBDA.

  Begin:
	cli				; Turn off interrupts

	mov	ax, BDA 		; BIOS Data Area segment
	mov	ds, ax
	mov	ax, [MEM]		; Get number of 4k pages avaliable
	shl	ax, 6			; AX = Base segment of EBDA.
	mov	sp,  0e00h		; We want 3584 pages of stack space
	sub	ax, sp			; Determine were base of stack is going to be
	mov	ss, ax
	shl	sp, 4			; SP *= 16

	sti				; Turn interrupts back on

    ; As this stack will persist for sometime, my probe apart from checking SP gets
    ; near zero, will be able to scan buffer to see how deep BIOS and other routines
    ; have penetrated space.
	
	mov	es, ax
	or	ax, -1
	mov	cx, sp
	shr	cx, 1			; CX /= 2 (# of words on stack)
	xor	di, di
	rep	stosw

    ; Move 14 values saved in pre-amble to TOS so they're not overwitten by
    ; kernel load.

	mov	cx, 28			; 14 words * 2 = 28 bytes
	sub	sp, cx
	mov	di, sp			; ES:DI = Poiner to destination
	mov	ds, bx			; DS:SI = Source (SI was set earlier)
	shr	cx, 1			; CX /= 4 = 7 (moving dwords)
	rep	movsw

    ; Although I won't be using LEAVE. I'm going to set BP as it were a procedure frame

	mov	bp, sp			; BP + 2 = Pointer to saved registers
	push	dx			; BP - 2 = Drive number

	mov	ax, COLOR		; Set 80 x 26 (color)
	int	VIDEO

	mov	ax, cs			; Get boot sectors segment
	mov	cx, SignOn
	shr	cx, 4			; Convert strings pointer to segment
	add	ax, cx
	mov	es, ax
	mov	ds, ax			; Initialize segments to codes data area.

	or	cx, -1			; CX = Buffer size
	mov	di, cx
	inc	di			; ES:DI points to prompt string now
	mov	si, di			; Point to base of string.
	mov	al, 0			; Search mask
	repnz	scasb			; Find first occurence of AL in prompt string
	inc	cx
	inc	cx			; Bump back to character just before NULL
	neg	cx			; CX = length of ASCII string

	mov	dx, 450h
	sub	dl, cl
	shr	dl, 1
	mov	ah, SET_CUR_POS
	mov	bh, 0
	int	VIDEO

	call	ShowS

	inc	dh			; Bump to next row
	mov	ah, SET_CUR_POS
	int	VIDEO

	mov	al, 0FEH
	mov	bl, 0ECH
	mov	ah, WRITE_CHARS
	int	VIDEO

    ; Determine if this computer is long mode capable
	
	pushfd
	pop	eax
	mov	ecx, eax		; Save copy
	btc	eax, 21 		; Invert ID bit (21)
	push	eax
	popfd				; Write flags back
		
    ; ------------
    ; Test CPUID
    ; ------------

	pushfd
	pop	eax
	push	ecx
	popfd
	xor	eax, ecx		; See if ID flag has changed
	jz	@F
		
	xor	eax, eax
	bts	eax, 31
	push	eax
	cpuid
	pop	ecx
	cmp	eax, ecx
	jb	@F
		
	mov	al, 1			; EAX = 80000001
	cpuid
	bt	edx, 29 		; Test LM bit
	jc	MovBlk			; Not =al, pointer hasn't wrapped.
			
   @@:	call	ShowS
	jmp	ErrEnt

  MovBlk:
	mov	cx, Block_Size
	shr	cx, 2			; Number of words we'll be moving
	push	cx
	shr	cx, 2			; CX = # of 16 byte segments

	mov	ax, KERNEL_SEG		; Where sectors 2 - n are going to be loaded
	sub	ax, cx			; Leave room for kernel loader
	mov	es, ax
	xor	di, di

	mov	cx, MovSeg		; Source buffer
	shr	cx, 4			; Determine its segment
	mov	ax, cs
	add	ax, cx
	mov	ds, ax
	xor	si, si

	pop	cx
	rep	movsd

    ; Because code can change, I wanted to make this algo dynamic, thus the RETF.
	
	mov	ax, es			; Build far pointer
	push	ax

	push	0
	retf				; Jump to it

		align	16

  MovSeg:
	sub	sp, PACKET_SIZE 	; Space for disk parameters
	mov	ax, ss
	mov	ds, ax
	mov	si, sp			; DS:SI = Pointer to parameters
	mov	ah, Get_D_Params
	mov	dx, [bp - 2]		; Get drive # again
	int	DISK_IO
	jc	ErrEnt

  ; Load in remaining sectors of this track/head

	mov	ax, KERNEL_SEG
	mov	es, ax
	xor	bx, bx			; ES:BX = Pointer to desintation buffer
	mov	cx, 2
	mov	al, [si + 12]
	dec	al
	mov	ah, READ_SECT
	int	DISK_IO
	jc	ErrEnt

	add	sp, PACKET_SIZE 	; Recover space used for metrics
	jmp	KERNEL_SEG:0

    ; If we get to here, then our loader failed somewhere

		align	8

  ErrEnt:
	mov	ax, VIDEO_SEG		; Point to
	mov	es, ax
	mov	di, 12
	mov	cx, 4
	mov	ax, 0xe4fe
	rep	stosw

	xor	ax, ax			; Wait for operator input
	int	KEYBOARD

	int	0x19			; Re-boot

		align	16

  Block_Size	=     $ - MovSeg

; --------------------------------------------------------------------------------------------

  SignOn:	db	'Any-Name   Loader', 0
		db	10, 10, 13
		db	9, 'Not 64 bit', 0

		db	510 - $   dup (-1)

		dw	0xAA55		 ; Boot signature