;; -----------------------------------------------------------------------
;;
;;   Copyright 1994-2008 H. Peter Anvin - All Rights Reserved
;;
;;   This program is free software; you can redistribute it and/or modify
;;   it under the terms of the GNU General Public License as published by
;;   the Free Software Foundation, Inc., 53 Temple Place Ste 330,
;;   Boston MA 02111-1307, USA; either version 2 of the License, or
;;   (at your option) any later version; incorporated herein by reference.
;;
;; -----------------------------------------------------------------------

;;
;; parseconfig.inc
;;
;; Configuration file operations
;;

		section .text
;
; "default" command
;
pc_default:	mov di,default_cmd
		call getline
		mov byte [di-1],0		; null-terminate
		ret

;
; "ontimeout" command
;
pc_ontimeout:	mov di,Ontimeout
		call getline
		sub di,Ontimeout+1		; Don't need final space
		mov [OntimeoutLen],di
		ret

;
; "onerror" command
;
pc_onerror:	mov di,Onerror
		call getline
		sub di,Onerror
		mov [OnerrorLen],di
		ret

;
; "append" command
;
pc_append:      cmp byte [VKernel],0
		ja .vk
                mov di,AppendBuf
		call getline
                sub di,AppendBuf
.app1:		mov [AppendLen],di
		ret
.vk:		mov di,VKernelBuf+vk_append	; "append" command (vkernel)
		call getline
		sub di,VKernelBuf+vk_append
                cmp di,byte 2
                jne .app2
                cmp byte [VKernelBuf+vk_append],'-'
                jne .app2
                xor di,di			; If "append -" -> null string
.app2:		mov [VKernelBuf+vk_appendlen],di
		ret

;
; "ipappend" command (PXELINUX only)
;
%if IS_PXELINUX
pc_ipappend:	call getint
		jc .err
		cmp byte [VKernel],0
		jne .vk
		mov [IPAppend],bl
.err:		ret
.vk:		mov [VKernelBuf+vk_ipappend],bl
		ret
%endif

;
; "localboot" command (PXELINUX, ISOLINUX)
;
%if IS_PXELINUX || IS_ISOLINUX
pc_localboot:	call getint
		cmp byte [VKernel],0		; ("label" section only)
		je .err
		mov di,VKernelBuf+vk_rname
		xor ax,ax
		mov cx,FILENAME_MAX
		rep stosb			; Null kernel name
%if IS_PXELINUX
		; PXELINUX uses the first 4 bytes of vk_rname for the
		; mangled IP address
		mov [VKernelBuf+vk_rname+5], bx	; Return type
%else
		mov [VKernelBuf+vk_rname+1], bx	; Return type
%endif
.err:		ret
%endif

;
; "kernel", "config", ... command
;
pc_kernel:	cmp byte [VKernel],0
		je .err				; ("label" section only)
		mov [VKernelBuf+vk_type],al
		call pc_getline
		mov di,VKernelBuf+vk_rname
		call mangle_name
.err:		ret

;
; "timeout", "totaltimeout" command
;
; N.B. 1/10 s ~ 1.D2162AABh clock ticks
;
pc_timeout:	push ax
		call getint
		pop si
		jc .err
		mov eax,0D2162AABh
		mul ebx				; clock ticks per 1/10 s
		add ebx,edx
		mov [si],ebx
.err:		ret


;
; "totaltimeout" command
;
pc_totaltimeout:

;
; Generic integer variable setting commands:
; "prompt", "implicit"
;
pc_setint16:
		push ax
		call getint
		pop si
		jc .err
		mov [si],bx
.err:		ret

;
; Generic file-processing commands:
; "font", "kbdmap",
;
pc_filecmd:	push ax				; Function to tailcall
		call pc_getline
		mov di,MNameBuf
		call mangle_name
		call searchdir
		jnz .ok
		pop ax				; Drop the successor function
.ok:		ret				; Tailcall if OK, error return

;
; Commands that expect the file to be opened on top of the getc stack.
; "display", "include"
;
pc_opencmd:	push ax				; Function to tailcall
		call pc_getline
		mov di,MNameBuf
		call mangle_name
		call open
		jnz .ok
		pop ax				; Drop the successor function
.ok:		ret				; Tailcall if OK, error return

;
; "include" command (invoked from pc_opencmd)
;
pc_include:	inc word [IncludeLevel]
.err:		ret

;
; "serial" command
;
pc_serial:	call getint
		jc .err
		push bx				; Serial port #
		call skipspace
		jnc .ok
		pop bx
.err:		ret
.ok:
		call ungetc
		call getint
		mov [FlowControl], word 0	; Default to no flow control
		jc .nobaud
.valid_baud:
		push ebx
		call skipspace
		jc .no_flow
		call ungetc
		call getint			; Hardware flow control?
		jnc .valid_flow
.no_flow:
		xor bx,bx			; Default -> no flow control
.valid_flow:
		and bh,0Fh			; FlowIgnore
		shl bh,4
		mov [FlowIgnore],bh
		mov bh,bl
		and bx,0F003h			; Valid bits
		mov [FlowControl],bx
		pop ebx				; Baud rate
		jmp short .parse_baud
.nobaud:
		mov ebx,DEFAULT_BAUD		; No baud rate given
.parse_baud:
		pop di				; Serial port #
		cmp ebx,byte 75
		jb .err				; < 75 baud == bogus
		mov eax,BAUD_DIVISOR
		cdq
		div ebx
		mov [BaudDivisor],ax
		push ax				; Baud rate divisor
		cmp di,3
		ja .port_is_io			; If port > 3 then port is I/O addr
		shl di,1
		mov di,[di+serial_base]		; Get the I/O port from the BIOS
.port_is_io:
		mov [SerialPort],di

		;
		; Begin code to actually set up the serial port
		;
		lea dx,[di+3]			; DX -> LCR
		mov al,83h			; Enable DLAB
		call slow_out

		pop ax				; Divisor
		mov dx,di			; DX -> LS
		call slow_out

		inc dx				; DX -> MS
		mov al,ah
		call slow_out

		mov al,03h			; Disable DLAB
		inc dx				; DX -> LCR
		inc dx
		call slow_out

		in al,dx			; Read back LCR (detect missing hw)
		cmp al,03h			; If nothing here we'll read 00 or FF
		jne .serial_port_bad		; Assume serial port busted
		dec dx
		dec dx				; DX -> IER
		xor al,al			; IRQ disable
		call slow_out

		inc dx				; DX -> FCR/IIR
		mov al,01h
		call slow_out			; Enable FIFOs if present
		in al,dx
		cmp al,0C0h			; FIFOs enabled and usable?
		jae .fifo_ok
		xor ax,ax			; Disable FIFO if unusable
		call slow_out
.fifo_ok:

		inc dx
		inc dx				; DX -> MCR
		in al,dx
		or al,[FlowOutput]		; Assert bits
		call slow_out

		; Show some life
		cmp byte [SerialNotice],0
		je .notfirst
		mov byte [SerialNotice],0

		mov si,syslinux_banner
		call write_serial_str
		mov si,copyright_str
		call write_serial_str
.notfirst:
		ret

.serial_port_bad:
		mov [SerialPort], word 0
		ret

;
; "F"-key command
;
pc_fkey:	push ax
		call pc_getline
		pop di
		call mangle_name		; Mangle file name
		ret

;
; "label" command
;
pc_label:	call commit_vk			; Commit any current vkernel
		mov di,VKernelBuf		; Erase the vkernelbuf for better compression
		mov cx,(vk_size >> 1)
		xor ax,ax
		rep stosw
		call pc_getline
		mov di,VKernelBuf+vk_vname
		call mangle_name		; Mangle virtual name
		mov byte [VKernel],1		; We've seen a "label" statement
		mov si,VKernelBuf+vk_vname	; By default, rname == vname
		; mov di,VKernelBuf+vk_rname	; -- already set
		mov cx,FILENAME_MAX
		rep movsb
                mov si,AppendBuf		; Default append==global append
                mov di,VKernelBuf+vk_append
                mov cx,[AppendLen]
                mov [VKernelBuf+vk_appendlen],cx
                rep movsb
%if IS_PXELINUX					; PXELINUX only
		mov al,[IPAppend]		; Default ipappend==global ipappend
		mov [VKernelBuf+vk_ipappend],al
%endif
		ret

;
; "say" command
;
pc_say:		call pc_getline			; "say" command
		call writestr
		jmp crlf			; tailcall

;
; "text" command; ignore everything until we get an "endtext" line
;
pc_text:	call pc_getline			; Ignore rest of line
.loop:
		call pc_getline
		jc .eof

		; Leading spaces are already removed...
		lodsd
		and eax,0xdfdfdfdf		; Upper case
		cmp eax,'ENDT'
		jne .loop
		lodsd
		and eax,0x00dfdfdf		; Upper case and mask
		cmp eax,'EXT'
		jne .loop
		; If we get here we hit ENDTEXT
.eof:
		ret

;
; Comment line
;
pc_comment:	; Fall into pc_getline

;
; Common subroutine: load line into trackbuf; returns with SI -> trackbuf
; CF is set on EOF.
;
pc_getline:	mov di,trackbuf
		push di
		call getline
		mov byte [di],0			; Null-terminate
		pop si
		ret

;
; Main loop for configuration file parsing
;
parse_config:
		mov di,VKernelBuf		; Clear VKernelBuf at start
		xor ax,ax
		mov cx,vk_size
		rep stosb

.again:
		call getcommand			; Parse one command
                jnc .again			; If not EOF...
		call close
		dec word [IncludeLevel]		; Still parsing?
		jnz .again

		;
		; The fall through to commit_vk to commit any final
		; VKernel being read
		;
;
; commit_vk: Store the current VKernelBuf into buffer segment
;
commit_vk:
		; For better compression, clean up the append field
		mov ax,[VKernelBuf+vk_appendlen]
		mov di,VKernelBuf+vk_append
		add di,ax
		mov cx,max_cmd_len+1
		sub cx,ax
		xor ax,ax
		rep stosb

		; Pack into high memory
		mov si,VKernelBuf
		mov edi,[VKernelEnd]
		mov cx,vk_size
		call rllpack
		mov [VKernelEnd],edi
		ret
.overflow:
		mov si,vk_overflow_msg
		call writestr
		ret

		section .data
vk_overflow_msg	db 'Out of memory parsing config file', CR, LF, 0
SerialNotice	db 1			; Only print this once

		section .bss
		alignb 4
VKernelEnd	resd 1			; Lowest high memory address used

		; This symbol should be used by loaders to indicate
		; the highest address *they* are allowed to use.
HighMemRsvd	equ VKernelEnd
					; by vkernels
		section .config
		align 4, db 0
KbdTimeout      dd 0                    ; Keyboard timeout (if any)
TotalTimeout	dd 0			; Total timeout (if any)
AppendLen       dw 0                    ; Bytes in append= command
OntimeoutLen	dw 0			; Bytes in ontimeout command
OnerrorLen	dw 0			; Bytes in onerror command
CmdLinePtr	dw cmd_line_here	; Command line advancing pointer
ForcePrompt	dw 0			; Force prompt
NoEscape	dw 0			; No escape
AllowImplicit   dw 1                    ; Allow implicit kernels
AllowOptions	dw 1			; User-specified options allowed
IncludeLevel	dw 1			; Nesting level
SerialPort	dw 0			; Serial port base (or 0 for no serial port)
VKernel		db 0			; Have we seen any "label" statements?

%if IS_PXELINUX
IPAppend	db 0			; Default IPAPPEND option
%endif

		section .uibss
                alignb 4		; For the good of REP MOVSD
command_line	resb max_cmd_len+2	; Command line buffer
		alignb 4
default_cmd	resb max_cmd_len+1	; "default" command line

%include "rllpack.inc"
