    %include	'Include\Stork.inc'

	USE16
; =============================================================================================
; Strictly for curiosities sake, preserve general purpose and segment registers so thier state
; as passed by BIOS can be displayed later.
; ---------------------------------------------------------------------------------------------

	pusha				; AX CX DX BX SP BP SI DI

	push	cs
	push	ds
	push	es
	push	ss
	push	fs			; Stack now has 14 word values, 16 assuming the
	push	gs			; the first two is a far pointer back into BIOS.

	jmp	BOOT_SEG:Begin
	
    ; Determine number of 4k blocks up to EBDA.

  Begin:
	mov	ax, BDA 		; AX = 40H
	mov	ds, ax			; DS points to BIOS Data Area
	mov	ax, [MEM]		; Get # of 4k blocks from 40:13
	shl	ax, 6			; Convert to segment value, AX *= 64

    ; Create a 64k block of data for scratch and stack and initialize it to -1's. This is
    ; to facilitate stack probbing in the event of memory leaks in stack space.

	sub	ah, 16			; Equivalent to EAX -= 0x1000
	mov	es, ax			; Set ES for STOSD instruction
	or	eax, -1 		; Fill pattern for 64k block
	mov	cx, 0x4000		; Number of dwords to be written
	xor	di, di			; ES:DI Points to beginning of block
	rep	stosd			; Fill buffer

    ; C0 (192) bytes of this buffer is going to be a scatch area pointed to by SS:BP.
    ; The 16 word values on stack are going to be moved to new TOS

	sub	di, 192 + 32		; ES:DI = TOS - 16 words
	push	ss
	pop	ds			; DS = Current stack segment
	mov	si, sp			; DS:SI points to values previously save
	mov	cl, 16
	rep	movsw			; Move values saved in preamble

    ; New BP and SP to be established.

	mov	bp, di			; DI points to scratch area @ FF40
	mov	ax, es

    ; New stack pointer can be set and we want to make sure an interrupt doesn't happen
    ; at the same time.

	cli
	mov	sp, di
	sub	sp, 32
	mov	ss, ax
	sti				; New stack established.

; =============================================================================================
; Load remainder of track at 7E00 assuming it is a 360k low density disk with only 8
; sectors / track. In effect this gives us a 4K boot loader.
; ---------------------------------------------------------------------------------------------

	mov	ax, cs
	mov	ds, ax			; Set data segment for block move later
	add	al, 32
	mov	es, ax
	xor	bx, bx			; ES:BX points to buffer @ 7E0:0000
	mov	dl, [bp-BOOT_DRIVE]	; Drive designator passed by BIOS
	mov	cx, 2			; CH = 0 (Head #), CL start @ second sector
	mov	ax, D_READ * 256 + 7	; AH = Read and AL for next 7 sectors
	int	DISK_IO
	or	ah, ah			; Test if there was an error
	jz	.Ok

    ; As it is unlikely load failed, I'm just going to display two RED flashing bars in middle
    ; of screen on error.
    
    	mov	ax, 0xb800		; Base of 80 x 25 video
    	mov	es, ax
    	mov	di, (12 * 80 + 37) * 2	; Center of screen
    	
    	mov	ax, 0x8cdb		; Large block / flashing red
    	
    	stosw				; Two blocks left of center
    	stosw
    	inc	di			; bounce over middle position
    	inc	di
    	stosw				; Two blocks right of center.
    	stosw
    	
    ; User just needs to change media and press reset/cold boot etc.
    
	hlt
	jmp	$ - 1			; Spin indefinately

; =============================================================================================
; Now I'm going to move this 4k image (7d00:0 - 7c00:0) starting at segment, EXT_IMG. There is
; no real logical reason for doing this other than I could. May be of interest to those
; interested in segmented memory in real mode.
; ---------------------------------------------------------------------------------------------

   .Ok:	xor	si, si			; DS:SI points to beginning of this code
	mov	ax, EXT_IMG
	mov	es, ax
	mov	di, si			; ES:DI points to buffer in low memory
	mov	cx, 2 * 512		; Total size of this code block
	rep	movsd			; Move 1024 dwords = 4,096 or 8 512 byte sectors

	jmp	EXT_IMG:Do_Map		; Branch to this code at its new location

; =============================================================================================
; A maximum entry count needs be calculated by EXT_IMG + (Do_Map.0 / 16) - MAP_BASE. At time
; of writing this would be 6A + (C0 >> 4) - 50 = 26H (38) 32 byte entries. E820 only needs 20
; or 24, but I'm padding to make display algo simpler later.
; ---------------------------------------------------------------------------------------------

Do_Map: mov	cx, cs			; CX = EXT_IMG. see Stork.inc
	mov	ax, .0			; This address is alread evenly aligned to 32 bytes
	shr	ax, 4			; Convert to segment
	add	cx, ax			; Calculate offset from beginning of sector
	mov	ax, MAP_BASE	
	sub	cx, ax
	shr	cx, 1			; CX /= 2, Actual number of entries.
	mov	[MAX_ENTRY], cx		; Maximum number of entries
	
    ; Map creation needs DS & ES to be equal and DI = 0, pointing to first entry.
    
	mov	es, ax
	mov	ds, ax			; Segment pointers must be the same
	xor	eax, eax
	shl	cx, 3			; CX *= 4, Number of dwords in buffer
	mov	di, ax
	push	di			; Need this pointer again after E820 buffer filled

    ; Looks bizzare, but everything from EXT_IMG to Do_Map.0 is going to be overwritten
    ; with nulls so buffer need to be aligned to an even 32 byte boundry, the size
    ; of each entry.
    
	align	32
    .0: rep	stosd			; 0500H to 2300 fill with nulls (more than enough)

; =============================================================================================
; http://wiki.osdev.org/Detecting_Memory_(x86)#BIOS_Function:_INT_0x15.2C_EAX_.3D_0xE820
; for details.
; ---------------------------------------------------------------------------------------------

    ; Initialize DI, EBX, ECX & EDX
    
	pop	di			; ES:DI points to base of E820 map
	mov	ebx, eax		; Continuation value initially null.
	mov	edx, 'PAMS'		; Map signature
	mov	ecx, eax
	jmp	GetMData.Start		; Bounce over local routine for int 15H algo.

    ; This local subroutine essentially save 20 bytes of code. Every bit counts when
    ; restricted to 512.

  GetMData:
	push	edx			; Preserve signature
	mov	cl, 24			; Some BIOS may have extended lengths
	mov	eax, 0xE820
	inc	byte [di + 20]
	int	SYSTEM_SERVICE
	pop	edx			; Restore signature
	ret

    ; This is only called once as there is a set of specific errors associated with first
    ; invocation.

    .Start: 
    	call	GetMData		; Initial call to function    
	jc	.Done			; If CF on first pass, no map
	cmp	eax, edx
	jne	.Done			; If not "SMAP" error
	test	ebx, ebx
	jz	.Done			; EBX = 0 No entries

    ; Top of loop, keep calling function until EBX returns null or an error occurs

 .Next:	jcxz	.Skip			; Function error
	cmp	cl, 20
	jbe	.0
	test	byte [di + 20], 1
	je	.Skip
	
    ; Ignore entries with null length
    
    .0: mov	eax, [di+8]
	or	eax, [di+12]		; Test 64 bit entry length
	jz	.Skip			; ZF = 1, Ignore entry
	
    ; Bump counter and exit on overflow
    
	inc	byte [COUNT]	      	; Bump entry count
	add	di, 32			; Point to next entry
	mov	ax, [MAX_ENTRY]
	sub	al, ah
	jz	.Done			; ZF = 1, no room left

 .Skip:	test	ebx, ebx
	jz	.Done			; ZF = 1, then list is complete
	call	GetMData		; Read another entry
	jnc	.Next			; CF = 0, continue

	or	byte [MAX_ENTRY], -1	; Turn all bits on to indicate proc failure

    ; If MAX_ENTRY = COUNT, then we probably don't have all entries (overflow)
    ;    MAX_ENTRY = -1     then function failed at COUNT - 1
    	
 .Done: xor	eax, eax		; Clear bits 31 - 16
	mov	ax, es
	shl	ax, 4
	mov	[E820_PNTR], eax	; Write long pointer to scratch area

; =============================================================================================
; We could just simply turn the gate on, but I would like to know which BIOS already have
; gate on and which don't.
; ---------------------------------------------------------------------------------------------

  Check_A20:
	mov	byte [A20_LINE], GREEN	; A20 was already on
	mov	cx, 0x8000		; Try turning A20 on 32,768 times

    ; This sets up pointers for what is known as the wrap around method. In this case
    ; DS:SI (DS:0xD2) will point to "GetMData" and ES:DI (ES:0xD2) if gate is on

	mov	si, cs
	shl	si, 4
	add	si, GetMData		; Just an abritrary spot that can be overwitten.
	mov	di, si
	add	di, 16
	xor	ax, ax
	mov	ds, ax
	dec	ax
	mov	es, ax

    ; Take value at memory location, negate it and write it back. If it's not the same
    ; then gate is on

 .Test: push	di
	push	si	
	lodsw
	neg	ax
	stosw
	cmp	[si-2], ax
	pop	si
	pop	di
	jnz	Exit

    ; This fast A20 gate mentod works on my test system, Phoenix TrustedCore Desktop SP2
    ; for ThinkCenter.

	mov	byte [A20_LINE], YELLOW ; A20 turned off with fast method
	in	al, 0x92
	or	al, 2
	out	0x92, al
	dec	cx
	jnz	.Test
	
;  *** There is 142 bytes space left to code an alternate method to turn A20 on. ***

	mov	byte [A20_LINE], RED  	; Indication that A20 failed		

	times 505-$+$$ db 144		; Padd space with NOP's 0x90
	
 Exit: 	jmp	EXT_IMG + 32:0		; Branch to next sector	
	dw	0xAA55			; Boot signature