;
; Center.asm
; Center colored string in UEFI in assembly
; Flat Assembler v1.71.54
; 2016 MrFox
;

; Symbols and structures

; The UEFI favorite data type: Unsigned Integer Native width
; (32-bit width in 32-bit systems, 64-bit in 64-bit systems)
; I use 32-bit width because my EFI is 32-bit (rd)
; Data alignment must be same as width
; (all UEFI data stuff must be native aligned)
struc UINTN {
  align 4
  . rd 1
}

; Error codes

EFI_SUCCESS		= 0
EFIERR			= 0x80000000
EFI_LOAD_ERROR		= EFIERR or 1
EFI_INVALID_PARAMETER	= EFIERR or 2
EFI_UNSUPPORTED 	= EFIERR or 3
; Etc (more error codes in EFI spec)

; Console Colors

EFI_RED 		= 0x04
EFI_YELLOW		= 0x0E
EFI_BACKGROUND_BLUE	= 0x10
EFI_BACKGROUND_GREEN	= 0x20
EFI_BACKGROUND_RED	= 0x40
; more colors in the Specification


; SystemTable structure
virtual at 0
EFI_SYSTEM_TABLE:
 ; Table header
 .Signature		rq	1
 .Revision		UINTN
 .HeaderSize		UINTN
 .CRC32 		UINTN
 .Reserved		UINTN
 
 ; Rest of the table
 .FirmwareVendor	UINTN
 .FirmwareRevision	UINTN
 .ConsoleInHandle	UINTN
 .ConIn 		UINTN
 .ConsoleOutHandle	UINTN
 .ConOut		UINTN	; We need this one to handle screen output
 .StandardErrorHandle	UINTN
 .StdErr		UINTN
 .RuntimeServices	UINTN
 .BootServices		UINTN
 .NumberOfTableEntries	UINTN
 .ConfigurationTable	UINTN
end virtual

; Simple Text Output is a Protocol (like just a set of functions).
; The ConOut pointer in SystemTable (above) points to the memory address
; where the table described just below is located.
; The table itself contains memory addresses to 'call'
; if we want to use a specific UIFI function

virtual at 0
SIMPLE_TEXT_OUTPUT:
 .Reset 			UINTN
 .OutputString			UINTN	; Calling this address will run this function
 .TestString			UINTN
 .QueryMode			UINTN
 .SetMode			UINTN
 .SetAttribute			UINTN
 .ClearScreen			UINTN
 .SetCursorPosition		UINTN
 .EnableCursor			UINTN
 .Mode				UINTN
end virtual

; This structure is used to get Viewport mode (GetMode function)
virtual at 0
SIMPLE_TEXT_OUTPUT_MODE:
 .MaxMode			UINTN
 .Mode				UINTN
 .Attribute			UINTN
 .CursorColumn			UINTN
 .CursorRow			UINTN
 .CursorVisible 		rb 1
end virtual


; Some initial format settings for the linker regarding
; the executable file format used by UEFI
format pe dll efi
entry main
section '.text' code executable readable

; Program entry point

main:
	push	ebp

	; 1. Get arguments that UEFI passed to my program

	; 1.1. Get ImageHandle
	mov	eax, [esp+8]
	mov	[ImageHandle], eax

	; 1.2. Get SystemTable
	mov	ebx, [esp+12]
	mov	[SystemTable], ebx


	; 2. Get screen width and height

	; 2.1. Get where ConOut protocol is located
	mov	ebx, [SystemTable]
	mov	ebp, [ebx+EFI_SYSTEM_TABLE.ConOut]

	; 2.2. Get Console viewport width and height

	; 2.2.1. Get CurrentScreenMode structure location
	mov	eax, [ebp+SIMPLE_TEXT_OUTPUT.Mode]

	; 2.2.2. Get Mode Number into ECX
	mov	ecx, [eax+SIMPLE_TEXT_OUTPUT_MODE.Mode]

	; 2.2.3. Load variables to receive data
	lea	eax, [ScreenHeight]
	lea	ebx, [ScreenWidth]

	; 2.2.4. Send arguments to function
	push	eax	; Height
	push	ebx	; Width
	push	ecx	; Mode number
	push	ebp	; *This
	call	[ebp+SIMPLE_TEXT_OUTPUT.QueryMode]
	add	esp, 16 ; Clean the stack

	; 3. Clear screen (to current colors, which are white on black I think)

	push	ebp
	call	[ebp+SIMPLE_TEXT_OUTPUT.ClearScreen]
	add	esp,4

	; 4. Set screen Background and Foreground

	mov	eax,EFI_YELLOW or EFI_BACKGROUND_BLUE
	push	eax
	push	ebp
	call	[ebp+SIMPLE_TEXT_OUTPUT.SetAttribute]
	add	esp,8

	; 5. Set Cursor Position to center

	; 5.1. Some math first
	mov	edx,[ScreenHeight]
	shr	edx,1		   ; Division by two
	mov	eax,[ScreenWidth]
	sub	eax,10		   ; Length of the string
	shr	eax,1		   ; Division by two

	; 5.2. Sending the coordinates to put the cursor
	push	edx		   ; Row
	push	eax		   ; Column
	push	ebp		   ; *This
	call	[ebp+SIMPLE_TEXT_OUTPUT.SetCursorPosition]
	add	esp,12

	; 6. Now it's time to print the text
	lea	eax, [Text]
	push	eax			; This is our text address
	push	ebp			; push ConOut address and call one of its functions
	call	[ebp+SIMPLE_TEXT_OUTPUT.OutputString]
	add	esp, 8		; restore stack pointer (UEFI uses C calling convention)

	; 7. Set the coordinates back to 0,0
	pushd	0		; Row
	pushd	0		; Column
	push	ebp		; *This
	call	[ebp+SIMPLE_TEXT_OUTPUT.SetCursorPosition]
	add	esp,12

	; 8. Set the colors to RED on BLACK

	pushd	EFI_RED
	push	ebp
	call	[ebp+SIMPLE_TEXT_OUTPUT.SetAttribute]
	add	esp,8


	; 8. Exit program
	mov	eax, EFI_SUCCESS
	pop	ebp
	ret

section '.data' data readable writeable

ImageHandle	dd	?
SystemTable	dd	?
ScreenWidth	dd	0
ScreenHeight	dd	0
Text		du	'Hello UEFI',0

section '.reloc' fixups data discardable
