##=============================================================================
##
##      vectors.S
##
##      x86 exception vectors
##
##=============================================================================
#####COPYRIGHTBEGIN####
#                                                                          
# -------------------------------------------                              
# The contents of this file are subject to the Red Hat eCos Public License 
# Version 1.1 (the "License"); you may not use this file except in         
# compliance with the License.  You may obtain a copy of the License at    
# http://www.redhat.com/                                                   
#                                                                          
# Software distributed under the License is distributed on an "AS IS"      
# basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.  See the 
# License for the specific language governing rights and limitations under 
# the License.                                                             
#                                                                          
# The Original Code is eCos - Embedded Configurable Operating System,      
# released September 30, 1998.                                             
#                                                                          
# The Initial Developer of the Original Code is Red Hat.                   
# Portions created by Red Hat are                                          
# Copyright (C) 1998, 1999, 2000, 2001 Red Hat, Inc.                             
# All Rights Reserved.                                                     
# -------------------------------------------                              
#                                                                          
#####COPYRIGHTEND####
##=============================================================================
#######DESCRIPTIONBEGIN####
##
## Author(s):   jskov
## Contributors:jskov
## Date:        1999-01-07
## Purpose:     x86 exception vectors
## Description: This file defines the code placed into the exception
##              vectors. It also contains the first level default VSRs
##              that save and restore state for both exceptions and
##              interrupts.
##
######DESCRIPTIONEND####
##
##=============================================================================


#include <pkgconf/system.h>
#include <pkgconf/hal.h>
#include CYGBLD_HAL_PLATFORM_H

#ifdef CYGPKG_KERNEL
#include <pkgconf/kernel.h>
#endif /* CYGPKG_KERNEL */

#include <cyg/hal/arch.inc>

#==============================================================================

    .file   "vectors.S"

#==============================================================================
# Real startup code. We jump here from the various reset vectors to set up the
# world.

	.text
	.globl	_start

_start: 
	hal_cpu_init
	hal_smp_init
	hal_diag_init
	hal_mmu_init
	hal_memc_init
	hal_intc_init
	hal_cache_init
	hal_timer_init

	# Loading the stack pointer seems appropriate now.
	
	movl	$__interrupt_stack, %esp

#if defined(CYG_HAL_STARTUP_FLOPPY) || defined(CYG_HAL_STARTUP_ROM)
	# If we are here first, initialize the IDT. RAM startup
	# configurations can assume that Redboot has already set
	# the IDT up.
	hal_idt_init
#endif	
						
	hal_mon_init

	# Init FPU
	# Do this after the monitor init so that we can plant our
	# own FPU unavailable VSR.
	
	hal_fpu_init		# WARNING: may adjust stack pointer
	
	# Zero the BSS. If the BSS is not a whole number of words
	# long we will write up to 3 extra bytes at the end.
	# (This should not be a problem usually).
	movl	$__bss_end,%ecx		# ECX = end of BSS
	movl	$__bss_start,%edi	# EDI = base of BSS
	subl	%edi,%ecx		# ECX = size of BSS
	addl	$3,%ecx			# ECX += sizeof(long)-1
	shrl	$2,%ecx			# ECX >>= 2 = number of words to fill
	xorl	%eax,%eax		# EAX = 0 = fill value
	rep     stosl			# Fill it in

#ifdef CYG_HAL_STARTUP_ROM

	# In a ROM booted system, we also need to copy the data section
	# out to the RAM.
	movl	$__rom_data_start,%esi	# ESI = base of ROM data area
	movl	$__ram_data_start,%edi	# EDI = base of RAM data area
	movl	$__ram_data_end,%ecx	# ECX = end of data
	subl	%edi,%ecx		# ECX = size of data in bytes
	shrl	$2,%ecx			# ECX >>= 2 = number of words to copy
	rep     movsl			# Copy it over

#endif	
	
	.extern hal_variant_init
	call	hal_variant_init

	.extern hal_platform_init
	call	hal_platform_init

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
	// This is here so we can debug the constructors.
	.extern initialize_stub
	call	initialize_stub
#endif

	.extern cyg_hal_invoke_constructors
	call	cyg_hal_invoke_constructors

		
	.extern cyg_start
	call	cyg_start

	# Hmm.  Not expecting to return from cyg_start.
1:	hlt
	jmp	1b

#==============================================================================
# Default exception VSR

	.align	4, 0xCC
	.globl	__default_exception_vsr
__default_exception_vsr:

	## We enter here with the CPU state still in the registers and:
	## 12(%esp)	EFLAGS pushed by hardware
	##  8(%esp)	CS pushed by hardware
	##  4(%esp)	PC pushed by hardware
	##  0(%esp)	vector number pushed by trampoline

	pusha			# save all registers

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
	mov	%esp,%ebp                 # save SP
	cmpl	$__stub_stack_base,%esp   # compare SP with stub stack base
	jb	1f                        # if sp < istack base, switch
	cmpl	$__stub_stack,%esp        # compare SP with stub stack top
	jbe	2f                        # if sp < stack top, dont switch
1:
	movl	$__stub_stack,%esp        # move to stub stack
	
	## We switched stacks with previous ESP in EBP
	## 44(%ebp)	EFLAGS pushed by hardware
	## 40(%ebp)	CS pushed by hardware
	## 36(%ebp)	PC pushed by hardware
	## 32(%ebp)	vector number pushed by trampoline
	## 28(%ebp)	EAX
	## 24(%ebp)	ECX
	## 20(%ebp)	EDX
	## 16(%ebp)	EBX
	## 12(%ebp)	(ESP - 16)
	##  8(%ebp)	EBP
	##  4(%ebp)	ESI
	##  0(%ebp)	EDI
	
	pushl	44(%ebp)		# copy EFLAGS from original stack
	pushl	40(%ebp)		# copy CS
	pushl	36(%ebp)		# copy PC
	pushl	32(%ebp)		# copy vector number
	pusha
	movl	8(%ebp),%eax		# copy EBP
	movl	%eax,8(%esp)
	movl	12(%ebp),%eax		# copy ESP
	movl	%eax,12(%esp)
2:
#endif
	hal_fpu_push_exc	# save FPU state

	mov	%esp,%edi	# save state pointer in EDI

        # adjust ESP by 16 for the state stored before the pusha
        add     $16,i386reg_esp(%edi)
		

	hal_fpu_push_exc_annex
	
	# Call exception handler
	.extern	cyg_hal_exception_handler
	pushl	%edi			# arg1 = saved state
	call	cyg_hal_exception_handler
	addl	$4,%esp			# pop arg
	
	hal_fpu_pop_exc_annex
		

	hal_fpu_pop_exc		# restore FPU state

	## At this point, the stack contains:
	## 44(%esp)	EFLAGS pushed by hardware
	## 40(%esp)	CS pushed by hardware
	## 36(%esp)	PC pushed by hardware
	## 32(%esp)	vector number pushed by trampoline
	## 28(%esp)	EAX
	## 24(%esp)	ECX
	## 20(%esp)	EDX
	## 16(%esp)	EBX
	## 12(%esp)	ESP
	##  8(%esp)	EBP
	##  4(%esp)	ESI
	##  0(%esp)	EDI
	
#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
	movl	12(%esp),%ebp	# pre-exception ESP
	sub	$48,%ebp	# adjust for current stack frame
	cmpl	%esp,%ebp
	je	1f

	## need to switch stacks
	xchg	%esp,%ebp
	add	$48,%esp
	pushl	44(%ebp)	# EFLAGS
	pushl	40(%ebp)	# CS
	pushl	36(%ebp)	# PC
	mov	%ebp,%esp
	popa
	movl	-20(%esp),%esp	# popa does not restore %esp
	sub	$12,%esp	# adjust for EFLAGS, CS, and PC
	iret	
1:
#endif
	popa			# restore all our registers.
	addl	$4, %esp	# skip the vector number
                                # Note: we do not need to re-adjust ESP
                                # back by 16 as popa does not pop ESP.
	iret			# and return to the program.
			
#==============================================================================
# Default interrupt VSR
#
#
	.extern	__interrupt_stack

	.align	4, 0xCC
	.globl	__default_interrupt_vsr
__default_interrupt_vsr:

	## We enter here with the CPU state still in the registers and:
	##  0(%esp)	vector number pushed by trampoline
	##  4(%esp)	PC pushed by hardware
	##  8(%esp)	CS pushed by hardware
	## 12(%esp)	EFLAGS pushed by hardware
	
	pusha			# save registers
	hal_fpu_push_int	# save FPU state

#if defined(CYGDBG_HAL_DEBUG_GDB_CTRLC_SUPPORT) || \
    defined(CYGDBG_HAL_DEBUG_GDB_BREAK_SUPPORT)
	# Save the context just to be able to set a breakpoint
	# when we have a CTRL-C
	.extern hal_saved_interrupt_state
	movl %esp,hal_saved_interrupt_state
#endif

#if defined(CYGFUN_HAL_COMMON_KERNEL_SUPPORT) && \
    !defined(CYGPKG_HAL_SMP_SUPPORT)
	# Increment scheduler lock
	.extern cyg_scheduler_sched_lock
	incl	cyg_scheduler_sched_lock
#endif

	movl	%esp,%ebp			# EBP = copy of ESP
	
        # adjust ESP by 16 for the state stored before the pusha
        add     $16,i386reg_esp(%esp)
		
	hal_to_intstack
		
	hal_fpu_push_int_annex			# save extra FPU state
	
#ifdef CYGSEM_HAL_COMMON_INTERRUPTS_ALLOW_NESTING

	# If we are allowing nested interrupts, restore the flags pushed
	# by the hardware when this interrupt was taken.
	
	movl	i386reg_eflags(%ebp), %eax
	btrl	$8,%eax			# Clear TF bit
	pushl	%eax
	popfl
#endif

#if defined(CYGPKG_KERNEL_INSTRUMENT) && defined(CYGDBG_KERNEL_INSTRUMENT_INTR)

	# Call cyg_instrument to record that this interrupt is being raised.

	movl	i386reg_vector(%ebp), %ecx # vector number from saved state
	movl	%ecx,%edi		# EDI = copy of vector
	subl	$0x20,%edi		# EDI = interrupt table offset
			
	pushl	%edi			# arg3 = interrupt number
	pushl	%ecx			# arg2 = vector number
	pushl	$0x0301			# arg1 = type = INTR.RAISE

	call	cyg_instrument		# call instrument function
	add	$12,%esp		# skip arguments
#endif
	
	# Call hal_interrupt_handlers[vector](vector, cyg_hal_interrupt_data[vector])
	
	movl	i386reg_vector(%ebp), %ecx # vector number from saved state
	movl	%ecx,%edi		# EDI = copy of vector
	subl	$0x20,%edi		# EDI = interrupt table offset
	movl	$hal_interrupt_handlers, %ebx
	movl	(%ebx, %edi, 4), %edx	# EDX = interrupt routine
	movl	$hal_interrupt_data, %ebx
	movl	(%ebx, %edi, 4), %eax	# EAX = interrupt data
	pushl	%eax			# arg2 = data
	pushl	%ecx			# arg1 = vector
	call	*%edx			# EAX = return value, needed for interrupt_end()
	addl	$8,%esp			# pop args

	# At this point:
	# EAX = ISR return code (returned by call)
	# EDI = ISR table offset (saved across call)
	# EBP = State pointer (saved across call)

	hal_fpu_pop_int_annex		# Pop any saved interrupt state
		
	# If we are returning from the last nested interrupt, move back
	# to the thread stack. interrupt_end() must be called on the
	# thread stack since it potentially causes a context switch.
	
	hal_from_intstack	
			
#ifdef CYGFUN_HAL_COMMON_KERNEL_SUPPORT

	# Call interrupt_end(r, cyg_hal_interrupt_objects[vector], regs)
	# - r is the return value from the ISR
	# - regs points to saved CPU context

	movl	$hal_interrupt_objects, %ebx
	movl	(%ebx, %edi, 4), %edx	# EDX = interrupt object ptr
	pushl	%ebp			# arg3 = ptr to saved registers
	pushl	%edx			# arg2 = object
	pushl	%eax			# arg1 = ISR return code
	call	interrupt_end		# Call it
	addl	$12, %esp		# pop args
	
#endif

	# Now pull saved state from stack and return to 
	# what thread was originally doing.
	
	hal_fpu_pop_int			# restore FPU state
	popa				# restore all our registers.
	addl	$4, %esp		# skip the vector number.
	iret				# and return to the thread.

#==============================================================================
## Execute pending DSRs on the interrupt stack with interrupts enabled.
## Note: this can only be called from code running on a thread stack so we 
## can always just jump to the interrupt stack without looking.

#ifdef CYGIMP_HAL_COMMON_INTERRUPTS_USE_INTERRUPT_STACK

	.extern cyg_interrupt_call_pending_DSRs
	.global hal_interrupt_stack_call_pending_DSRs
	
hal_interrupt_stack_call_pending_DSRs:	

	pushl	%ebp			# save EBP
	pushfl				# save flags
	movl	%esp,%ebp		# EBP = saved SP

	hal_load_istack	%edx		# load new SP into EDX
	movl	%edx,%esp		# move it to ESP
	
	sti				# Enable interrupts

	# Call back to kernel to run DSRs
	call	cyg_interrupt_call_pending_DSRs

	# On return EBP will still contain the old ESP since
	# it is a callee saved register.
	
	movl	%ebp,%esp		# restore saved SP

	# Now merge the original IF bit into the current
	# EFLAGS. 
	
	popl	%eax			# EAX = saved flags
	pushfl				# 0(%esp) = current flags
	btrl	$9,0(%esp)		# clear IF bit in current flags
	andl	$0x00000200,%eax	# isolate saved IF bit
	orl	%eax,0(%esp)		# OR it in to the saved flags
	popfl				# restore flags
	
	popl	%ebp			# restore EBP
	ret				# and return
	
#endif				

#==============================================================================
# FPU lazy state switch VSR
# This is invoked via hardware exception 7 (FPU unavailable) as a result of
# setting the TS bit in CR0 whenever we context switch. If we discover here
# that a different context from the current owner of the FPU has attempted
# a floating point operation, we save the old context and load the new before 
# allowing it to proceed.
	
#ifdef CYGHWR_HAL_I386_FPU_SWITCH_LAZY		

#ifndef CYGPKG_HAL_SMP_CPU_MAX
#define CYGPKG_HAL_SMP_CPU_MAX 1
#endif
				
	.data
	.global cyg_hal_fpustate_owner
cyg_hal_fpustate_owner:
	.rept	CYGPKG_HAL_SMP_CPU_MAX
	.long	0				# pointer to FPU owning context
	.endr
	.global cyg_hal_fpustate_current	
cyg_hal_fpustate_current:	
	.rept	CYGPKG_HAL_SMP_CPU_MAX
	.long	0				# pointer to current threads FPU context
	.endr
			
	.text	
__fpu_switch_vsr:

	## We enter here with the CPU state still in the registers and:
	##  0(%esp)	vector number pushed by trampoline
	##  4(%esp)	PC pushed by hardware
	##  8(%esp)	CS pushed by hardware
	## 12(%esp)	EFLAGS pushed by hardware
	
	clts					# clear CR0:TS bit
	pusha					# save all regs

	hal_smp_cpu %ecx			# ECX = CPU id
	movl	$cyg_hal_fpustate_owner,%esi	# ESI = FPU context owner table
	leal	0(%eax,%ecx,4),%esi		# ESI = address of owner pointer
	movl	$cyg_hal_fpustate_current,%edi	# EDI = current threads context table
	leal	0(%ebx,%ecx,4),%edi		# EDI = address of context pointer
	movl	0(%esi),%eax			# EAX = Current FPU context owner
	movl	0(%edi),%ebx			# EBX = Current threads FPU context
	cmpl	%ebx,%eax			# current == owner ?
	je	9f				# yes, nothing else to do
	cmpl	$0,%eax				# is FPU even in use?
	je	1f				# if not, skip save
	fsave	i386reg_fpucontext_state(%eax)	# save FPU state
#ifdef CYGHWR_HAL_I386_PENTIUM_SSE
	# Save SIMD state.

	# FIXME. This is awfully inefficient. Need to use FXSAVE to
	# save FPU and SIMD at same time. FXSAVE requires a 16 byte
	# alignment and does not have an implicit finit as does FSAVE.

	stmxcsr	i386reg_simd_mxcsr(%eax)
	movups	%xmm0,i386reg_simd_xmm0(%eax)
	movups	%xmm1,i386reg_simd_xmm1(%eax)
	movups	%xmm2,i386reg_simd_xmm2(%eax)
	movups	%xmm3,i386reg_simd_xmm3(%eax)
	movups	%xmm4,i386reg_simd_xmm4(%eax)
	movups	%xmm5,i386reg_simd_xmm5(%eax)
	movups	%xmm6,i386reg_simd_xmm6(%eax)
	movups	%xmm7,i386reg_simd_xmm7(%eax)
#endif
	movl	$1,i386reg_fpucontext_valid(%eax) # mark valid
1:
	movl	%ebx,0(%esi)            	# Set new owner
	btl	$0,i386reg_fpucontext_valid(%ebx) # Valid state?
	jc	2f				# If yes, go to restore it
	finit					# Otherwise init FPU
#ifdef CYGHWR_HAL_I386_PENTIUM_SSE
	# FIXME. Anything needed here?
#endif
	jmp	9f				# skip restore
2:
	frstor	i386reg_fpucontext_state(%ebx)	# restore FPU state
#ifdef CYGHWR_HAL_I386_PENTIUM_SSE
	# Restore SIMD state.

	# FIXME. This is awfully inefficient. Need to use FXRSTOR to
	# restore FPU and SIMD at same time. FXRSTOR requires a 16 byte
	# alignment.

	movups	i386reg_simd_xmm0(%ebx),%xmm0
	movups	i386reg_simd_xmm1(%ebx),%xmm1
	movups	i386reg_simd_xmm2(%ebx),%xmm2
	movups	i386reg_simd_xmm3(%ebx),%xmm3
	movups	i386reg_simd_xmm4(%ebx),%xmm4
	movups	i386reg_simd_xmm5(%ebx),%xmm5
	movups	i386reg_simd_xmm6(%ebx),%xmm6
	movups	i386reg_simd_xmm7(%ebx),%xmm7
	ldmxcsr	i386reg_simd_mxcsr(%ebx)
#endif
9:
	popa					# restore all our registers.
	addl	$4, %esp			# skip the vector number.
	iret					# and return to the thread.
		
#endif

#if 0			
#==============================================================================
# Assembler swap routines
	
		
# x = CPU_swap_u16(x)
	.align	4
	.global CPU_swap_u16
CPU_swap_u16:
	xorl	%eax, %eax
	movb	4(%esp), %ah
	movb	5(%esp), %al
	ret

# x = CPU_swap_u32(x)
	.align	4
	.global CPU_swap_u32
CPU_swap_u32:
	xorl	%eax, %eax
	movb	4(%esp), %ah
	movb	5(%esp), %al
	sall	$16, %eax
	movb	6(%esp), %ah
	movb	7(%esp), %al
	ret
#endif
		
#==============================================================================
# Exception trampolines
# IDT exception gates point to short code sequences that push the vector
# number on to the stack and then indirect via the VSR table to a handler.
# 

	# Just for yuks we keep a count of the number of times each
	# vector is called.
	.bss
	.globl hal_vsr_stats
hal_vsr_stats:	
	.rept 64	// Default VSR table is 64 entries long
	.long 0
	.endr

	# When an exception which supplies an error code is generated,
	# we move the code here. 
hal_trap_error_code:
	.long 0

	.text
	
	# macro to create exception handler (no error code)
	.macro  hal_pc_exception_noerr idx
hal_pc_exception_\idx:
	movl	$0,hal_trap_error_code
	pushl	$\idx
	incl	(hal_vsr_stats+\idx*4)
	jmp	*(hal_vsr_table+\idx*4)
	.endm

	# macro to create exception handler (with error code)
	.macro  hal_pc_exception_err idx
hal_pc_exception_\idx:
	popl	hal_trap_error_code
	pushl	$\idx
	incl	(hal_vsr_stats+\idx*4)
	jmp	*(hal_vsr_table+\idx*4)
	.endm

	# Now generate all the default exception VSR trampolines.
	
	hal_pc_exception_noerr  0
	hal_pc_exception_noerr  1
	hal_pc_exception_noerr  2
	hal_pc_exception_noerr  3	
	hal_pc_exception_noerr  4
	hal_pc_exception_noerr  5	
	hal_pc_exception_noerr  6
	hal_pc_exception_noerr  7	
	hal_pc_exception_err    8
	hal_pc_exception_noerr  9	
	hal_pc_exception_err   10
	hal_pc_exception_err   11	
	hal_pc_exception_err   12
	hal_pc_exception_err   13	
	hal_pc_exception_err   14
	hal_pc_exception_noerr 15	
	hal_pc_exception_noerr 16
	hal_pc_exception_err   17	
	hal_pc_exception_noerr 18
	hal_pc_exception_noerr 19	
	hal_pc_exception_noerr 20
	hal_pc_exception_noerr 21	
	hal_pc_exception_noerr 22
	hal_pc_exception_noerr 23	
	hal_pc_exception_noerr 24
	hal_pc_exception_noerr 25	
	hal_pc_exception_noerr 26
	hal_pc_exception_noerr 27	
	hal_pc_exception_noerr 28
	hal_pc_exception_noerr 29	
	hal_pc_exception_noerr 30
	hal_pc_exception_noerr 31	

#==============================================================================
# IRQ handler trampolines


	# macro to create exception handler (no error code)
	.macro  hal_pc_irq_handler idx
hal_pc_irq_\idx:
	pushl	$\idx
	incl	(hal_vsr_stats+\idx*4)
	jmp	*(hal_vsr_table+\idx*4)
	.endm

	hal_pc_irq_handler 32
	hal_pc_irq_handler 33
	hal_pc_irq_handler 34
	hal_pc_irq_handler 35
	hal_pc_irq_handler 36
	hal_pc_irq_handler 37
	hal_pc_irq_handler 38
	hal_pc_irq_handler 39
	hal_pc_irq_handler 40
	hal_pc_irq_handler 41
	hal_pc_irq_handler 42
	hal_pc_irq_handler 43
	hal_pc_irq_handler 44
	hal_pc_irq_handler 45
	hal_pc_irq_handler 46
	hal_pc_irq_handler 47


	# default vsr entries: pop the vector code from the stack and return.
default_vsr_iret:
	add $4,%esp
	iret

	# GNUPro apps use "int $0x80" for syscalls.
        # There is no vsr table entry for this.
	.global __syscall_tramp
__syscall_tramp:
	pushl $0x80
	jmp __default_exception_vsr

#==============================================================================
# Initial and interrupt stack

#ifndef CYG_HAL_I386_INTERRUPT_STACK_DEFINED
	        
	.bss

	
	.balign 16
	.global cyg_interrupt_stack_base
cyg_interrupt_stack_base:
__interrupt_stack_base:
	.rept CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
	.byte 0
	.endr
	.balign 16
	.global cyg_interrupt_stack
cyg_interrupt_stack:
__interrupt_stack:
        
        .long   0,0,0,0,0,0,0,0
#endif

#ifdef CYGDBG_HAL_DEBUG_GDB_INCLUDE_STUBS
	.global __stub_stack_base
__stub_stack_base:
	.rept CYGNUM_HAL_COMMON_INTERRUPTS_STACK_SIZE
	.byte 0
	.endr
	.balign 16
	.global __stub_stack
__stub_stack:
        
        .long   0,0,0,0,0,0,0,0
#endif


		
#------------------------------------------------------------------------------
# end of vectors.S
