/*[
call.c
LOCAL CHAR SccsID[]="@(#)call.c 1.15 02/27/95";
CALL CPU Functions.
-------------------
]*/
#include <insignia.h>
#include <host_def.h>
#include <xt.h>
#include <c_main.h>
#include <c_addr.h>
#include <c_bsic.h>
#include <c_prot.h>
#include <c_seg.h>
#include <c_stack.h>
#include <c_xcptn.h>
#include <c_reg.h>
#include <call.h>
#include <c_xfer.h>
#include <c_tsksw.h>
#include <fault.h>
/*
=====================================================================
EXTERNAL ROUTINES STARTS HERE.
=====================================================================
*/
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* Process far calls. */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
CALLF
#ifdef ANSI
(
IU32 op1[2] /* offset:segment pointer */
)
#else
(op1)
IU32 op1[2];
#endif
{
IU16 new_cs; /* The destination */
IU32 new_ip;
IU32 cs_descr_addr; /* code segment descriptor address */
CPU_DESCR cs_entry; /* code segment descriptor entry */
ISM32 dest_type; /* category for destination */
IU8 count; /* call gate count (if used) */
IU32 dpl; /* new privilege level (if used) */
IU16 new_ss; /* The new stack */
IU32 new_sp;
ISM32 new_stk_sz; /* Size in bytes of new stack */
IU32 ss_descr_addr; /* stack segment descriptor address */
CPU_DESCR ss_entry; /* stack segment descriptor entry */
/* Variables used on stack transfers */
IU32 old_cs;
IU32 old_ip;
IU32 old_ss;
IU32 old_sp;
IU32 params[31];
ISM32 i;
/* get destination (correctly typed) */
new_cs = op1[1];
new_ip = op1[0];
if ( GET_PE() == 0 || GET_VM() == 1 )
{
/* Real Mode or V86 Mode */
/* must be able to push CS:(E)IP */
validate_stack_space(USE_SP, (ISM32)NR_ITEMS_2);
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
/* do ip limit checking */
if ( new_ip > GET_CS_LIMIT() )
GP((IU16)0, FAULT_CALLF_RM_CS_LIMIT);
#else /* TAKE_REAL_MODE_LIMIT_FAULT */
/* The Soft486 EDL CPU does not take Real Mode limit failures.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
#endif /* TAKE_REAL_MODE_LIMIT_FAULT */
/* ALL SYSTEMS GO */
/* push return address */
spush16((IU32)GET_CS_SELECTOR());
spush((IU32)GET_EIP());
load_CS_cache(new_cs, (IU32)0, (CPU_DESCR *)0);
SET_EIP(new_ip);
}
else
{
/* protected mode */
/* decode and check final destination */
validate_far_dest(&new_cs, &new_ip, &cs_descr_addr, &count,
&dest_type, CALL_ID);
/* action possible types of target */
switch ( dest_type )
{
case NEW_TASK:
switch_tasks(NOT_RETURNING, NESTING, new_cs, cs_descr_addr, GET_EIP());
/* limit check new IP (now in new task) */
if ( GET_EIP() > GET_CS_LIMIT() )
GP((IU16)0, FAULT_CALLF_TASK_CS_LIMIT);
break;
case SAME_LEVEL:
read_descriptor_linear(cs_descr_addr, &cs_entry);
/* stamp new selector with CPL */
SET_SELECTOR_RPL(new_cs, GET_CPL());
/* check room for return address CS:(E)IP */
validate_stack_space(USE_SP, (ISM32)NR_ITEMS_2);
/* do ip limit check */
if ( new_ip > cs_entry.limit )
GP((IU16)0, FAULT_CALLF_PM_CS_LIMIT_1);
/* ALL SYSTEMS GO */
/* push return address */
spush16((IU32)GET_CS_SELECTOR());
spush((IU32)GET_EIP());
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
SET_EIP(new_ip);
break;
default: /* MORE_PRIVILEGE(0|1|2) */
read_descriptor_linear(cs_descr_addr, &cs_entry);
dpl = dest_type;
/* stamp new selector with new CPL */
SET_SELECTOR_RPL(new_cs, dpl);
/* find out about new stack */
get_stack_selector_from_TSS(dpl, &new_ss, &new_sp);
/* check new stack selector */
validate_SS_on_stack_change(dpl, new_ss,
&ss_descr_addr, &ss_entry);
/* check room for SS:(E)SP
parameters
CS:(E)IP */
new_stk_sz = count + NR_ITEMS_4;
validate_new_stack_space(new_stk_sz, new_sp, &ss_entry, new_ss);
/* do ip limit check */
if ( new_ip > cs_entry.limit )
GP((IU16)0, FAULT_CALLF_PM_CS_LIMIT_2);
/* ALL SYSTEMS GO */
SET_CPL(dpl);
/* update code segment */
old_cs = (IU32)GET_CS_SELECTOR();
old_ip = GET_EIP();
load_CS_cache(new_cs, cs_descr_addr, &cs_entry);
SET_EIP(new_ip);
/* 'pop' params from old stack */
old_ss = (IU32)GET_SS_SELECTOR();
old_sp = GET_ESP();
for ( i = 0; i < count; i++ )
params[i] = spop();
/* update stack segment */
load_SS_cache(new_ss, ss_descr_addr, &ss_entry);
if ( GET_OPERAND_SIZE() == USE16 )
SET_SP(new_sp);
else
SET_ESP(new_sp);
/*
FORM NEW STACK, VIZ
========== ==========
old SS:SP -> | parm 1 | new SS:SP -> | old IP |
| parm 2 | | old CS |
| parm 3 | | parm 1 |
========== | parm 2 |
| parm 3 |
| old SP |
| old SS |
==========
*/
/* push old stack values */
spush16(old_ss);
spush(old_sp);
/* push back params onto new stack */
for ( i = count-1; i >= 0; i-- )
spush(params[i]);
/* push return address */
spush16(old_cs);
spush(old_ip);
break;
}
}
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* call near indirect */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
CALLN
IFN1(
IU32, offset
)
{
/* check push to stack ok */
validate_stack_space(USE_SP, (ISM32)NR_ITEMS_1);
/*
Although the 386 book says a 16-bit operand should be AND'ed
with 0x0000ffff, a 16-bit operand is never fetched with the
top bits dirty anyway, so we don't AND here.
*/
/* do ip limit check */
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
if ( offset > GET_CS_LIMIT() )
GP((IU16)0, FAULT_CALLN_RM_CS_LIMIT);
#else /* TAKE_REAL_MODE_LIMIT_FAULT */
/* The Soft486 EDL CPU does not take Real Mode limit failures.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
#ifdef TAKE_PROT_MODE_LIMIT_FAULT
if ( GET_PE() == 1 && GET_VM() == 0 )
{
if ( offset > GET_CS_LIMIT() )
GP((IU16)0, FAULT_CALLN_PM_CS_LIMIT);
}
#endif /* TAKE_PROT_MODE_LIMIT_FAULT */
/* The Soft486 EDL CPU does not take Protected Mode limit failues
* for the instructions with relative offsets, Jxx, LOOPxx, JCXZ,
* JMP rel and CALL rel, or instructions with near offsets,
* JMP near and CALL near.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
#endif /* TAKE_REAL_MODE_LIMIT_FAULT */
/* all systems go */
spush((IU32)GET_EIP());
SET_EIP(offset);
}
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
/* call near relative */
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
GLOBAL VOID
CALLR
IFN1(
IU32, rel_offset
)
{
IU32 new_dest;
/* check push to stack ok */
validate_stack_space(USE_SP, (ISM32)NR_ITEMS_1);
/* calculate and check new destination */
new_dest = GET_EIP() + rel_offset;
if ( GET_OPERAND_SIZE() == USE16 )
new_dest &= WORD_MASK;
/* do ip limit check */
#ifdef TAKE_REAL_MODE_LIMIT_FAULT
if ( new_dest > GET_CS_LIMIT() )
GP((IU16)0, FAULT_CALLR_RM_CS_LIMIT);
#else /* TAKE_REAL_MODE_LIMIT_FAULT */
/* The Soft486 EDL CPU does not take Real Mode limit failures.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
#ifdef TAKE_PROT_MODE_LIMIT_FAULT
if ( GET_PE() == 1 && GET_VM() == 0 )
{
if ( new_dest > GET_CS_LIMIT() )
GP((IU16)0, FAULT_CALLR_PM_CS_LIMIT);
}
#endif /* TAKE_PROT_MODE_LIMIT_FAULT */
/* The Soft486 EDL CPU does not take Protected Mode limit failues
* for the instructions with relative offsets, Jxx, LOOPxx, JCXZ,
* JMP rel and CALL rel, or instructions with near offsets,
* JMP near and CALL near.
* Since the Ccpu486 is used as a "reference" cpu we wish it
* to behave a C version of the EDL Cpu rather than as a C
* version of a i486.
*/
#endif /* TAKE_REAL_MODE_LIMIT_FAULT */
/* all systems go */
spush((IU32)GET_EIP());
SET_EIP(new_dest);
}