📄 main_run.c
字号:
/* * Copyright (C) 1996-1998 by the Board of Trustees * of Leland Stanford Junior University. * * This file is part of the SimOS distribution. * See LICENSE file for terms of the license. * *//*************************************************************************** File: main_run.c** This code is called once to generate the main simulator loop and* assorted other routines. It is a semi-platform-independent rewrite* of the old MIPS main_run.s. Of course, we could also just make a* platform-independent MIPS assembler, or write it in some intermediate* representation.** I have also added in the callout code. It is easier to generate* everything in one place. Also we avoid name-space pollution with* the register variables a0, a1, etc..** In this implementation, assembly routines have become C routines which* generate the desired code at translator init time.*** To-do: there are a lot of repulsive mips-isms in this code, most* notably that unfortunate t9 convention. These need to be* eliminated and hidden in the vcode interface. Register* allocation needs to be re-thought. RA needs to be re-thought.* much to be done to make this truly portable. However, for now* we at least have a possibility of generating code for other* architectures.** $author$* $date$***************************************************************************/#include "c_port.h"#include "mips_arch.h" /* MIPS architecture specification */#include "embra.h"#include "callout.h" /* useful macros for common code sequences */#include "main_run.h"#include "mem_control.h"#include "cp0.h"#include "debug.h"#include "tc.h"#include "translator.h"#include "driver.h"#include "qc.h"#include "vcode.h" /* vcode types aXSnd macros */v_code *Main_Run_Buf; /* Pointer to space for our generated * * code. Allocated in tc.c */int Main_Run_Buf_Size; /* Size of code buffer *//* Guard against calls to uninitialized functions */static void bad(void){ CPUError("Embra: Call to uninitialized dynamic routine\n"); ASSERT(0);}vfptr EnterTC=(vfptr) bad, /* Pointers to generated routines */ EnterTC_CX=(vfptr) bad, IncCCasm=(vfptr) bad, GeneralEmEventPollasm= (vfptr) bad, EmEventPoll=(vfptr)bad, UPperiodic_callout=(vfptr)bad, Embra_CX=(vfptr)bad, Embra_CX_nochain=(vfptr)bad, continue_run_without_chaining=(vfptr)bad, continue_run=(vfptr)bad, SpillFP=(vfptr)bad, RestoreFP=(vfptr)bad, callout=(vfptr)bad;/* Memory reference wrappers */mr_ptr mem_ref_wrapper=(mr_ptr)bad;mar_ptr phys_mem_ref_wrapper=(mar_ptr)bad, pa_mem_ref_wrapper=(mar_ptr)bad;mar_ptr QC64HashFunc=(mar_ptr)bad, Em_dynPQCdsh=(mar_ptr)bad, Em_dynPQCdex=(mar_ptr)bad, Em_HackedIPageMMUQC=(mar_ptr)bad;/* simple static symbol table to preserve my sanity */typedef struct main_run_sym { char *name; void *addr;} main_run_sym;main_run_sym main_run_syms[] = { "Bad", (void *) &bad, "EnterTC", (void *) &EnterTC, "EnterTC_CX",(void *) &EnterTC_CX, "IncCCasm",(void *) &IncCCasm, "GeneralEmEventPollasm",(void *) &GeneralEmEventPollasm, "EmEventPoll",(void *) &EmEventPoll, "UPperiodic_callout",(void *) &UPperiodic_callout, "Embra_CX",(void *) &Embra_CX, "Embra_CX_nochain",(void *) &Embra_CX_nochain, "continue_run_without_chaining", (void *) &continue_run_without_chaining, "continue_run",(void *) &continue_run, "SpillFP",(void *) &SpillFP, "RestoreFP",(void *) &RestoreFP, "callout",(void *) &callout, "mem_ref_wrapper",(void *) &mem_ref_wrapper, "phys_mem_ref_wrapper",(void *) &phys_mem_ref_wrapper, "pa_mem_ref_wrapper",(void *) &pa_mem_ref_wrapper, "QC64HashFunc",(void *) &QC64HashFunc, "Em_dynPQCdsh",(void *) &Em_dynPQCdsh, "Em_dynPQCdex",(void *) &Em_dynPQCdex, "Em_HackedIPageMMUQC",(void *) &Em_HackedIPageMMUQC};extern v_reg_t VREGS[]; /* Allocated registers from translator.c */extern v_reg_t FVREGS[];static v_reg_t zero, sp, ra, a0, a1, a2, a3, s2, /* registers we use */ t0, t1, t2, t3, t4, t7, t8, t9, v0,v1, vss_base, /* = curEmp = EMP[curcpu] */ clock_reg, /* clock */ dhit_reg, /* cache registers */ ihit_reg, qc_reg, /* registers for memory addr quick check */ mmumask_reg, mmu_reg, sim_t1, sim_t2, sim_t4, branchreg, pc_reg; /* simulated PC *//* Interfaces to several routines we call or call out to * This should be cleaned up */extern void VC_Allocate_Regs(void); /* Allocate registers in VREGS[] */extern void IncCC(int cpuNum, int inc); /* Increment cycle count... Hmm.... parameters??? */extern void GeneralEmEventPoll(int cpuNum);extern void NonReturningProcReturned(void);extern EmbraState* CEmEventPoll(void);extern int callout_interval;extern MA Em_QC64Reload(VA vAddr, int flags ); TCA ChainBasicBlock( TCA jump_addr, VA new_pc);static v_label_t spill_fp_label; /* labels can't be automatic! ??? ;-( *//* InterfaceInitRegs(): initialize register variables, which * we will use in all of our vcode calls *//* XXX This register thing needs to be looked at. We need to * boil things down to the smallest possible set of registers. * Moreover, we need to get rid of MIPS-isms (e.g. t9, calling * conventions, etc.) or hide them within the vcode implementation * wherever possible. */void InterfaceInitRegs(void){ VC_Allocate_Regs(); zero = VREGS[0]; /* bad */ sp = VREGS[REG_SP]; ra = VREGS[REG_RA]; a0 = VREGS[REG_A0]; a1 = VREGS[REG_A1]; a2 = VREGS[REG_A2]; a3 = VREGS[REG_A3]; s2 = VREGS[REG_S2]; t0 = VREGS[REG_T0]; t1 = VREGS[REG_T1]; t2 = VREGS[REG_T2]; t3 = VREGS[REG_T3]; /* t4 = VREGS[REG_T4]; *** not used (yet?) ***/ /* t7 = VREGS[REG_T7]; *** not used (yet?) ***/ t8 = VREGS[REG_T8]; t9 = VREGS[REG_T9]; v0 = VREGS[REG_V0]; v1 = VREGS[REG_V1]; vss_base = VREGS[VSS_BASE]; clock_reg = VREGS[CLOCK_REG]; dhit_reg = VREGS[DHIT_REG]; ihit_reg = VREGS[IHIT_REG]; qc_reg = VREGS[QC_REG]; mmumask_reg = VREGS[MMUMASK_REG]; mmu_reg = VREGS[MMU_REG]; sim_t1 = VREGS[SIM_T1]; sim_t2 = VREGS[SIM_T2]; sim_t4 = VREGS[SIM_T4]; branchreg = VREGS[BRANCHREG]; pc_reg = VREGS[PC_REG];}/* VLOAD_COMMON_VARS(): load common registers, in this case * registers which are used for memory quick check */void VLOAD_COMMON_VARS(void){ v_ldui(qc_reg, vss_base, CACHE_AX_OFF); v_ldui(mmumask_reg, vss_base, SSREG2_OFF); v_ldui(mmu_reg, vss_base, MMU_OFF);}/* EnterTC: we load registers used by Embra, and jump to the * main run loop (without chaining, because we are entering from * C code rather than translated code) */vfptr GenEnterTC(char *buf, int size, int *used){ v_reg_type arg[1]; /* one argument */ v_lambda("EnterTC", "%u", arg, V_LEAF, (v_code *) buf, size); /* leave some stack space - 256 bytes for now XXX -BL */ v_addii(sp, sp, -512); v_movu(vss_base, arg[0]); /* initialize vss_base with curEmp */ VLOAD_COMMON_VARS(); v_ldui(clock_reg, vss_base, CCD_OFF); v_ldui(dhit_reg, vss_base, SDHIT_OFF); v_ldui(ihit_reg, vss_base, SIHIT_OFF); REG_LD_OP(pc_reg, vss_base, PC_OFF); ASSERT(continue_run_without_chaining != 0); v_jpi(continue_run_without_chaining); v_nop(); return v_end(used).v;}/* EnterTC_CX: similar to EnterTC, but do a context switch * at the end (for MPinUP) */ vfptr GenEnterTC_CX(char *buf, int size, int *used){ v_reg_type arg[1]; /* one argument */ v_lambda("EnterTC_CX", "%u", arg, V_LEAF, (v_code *) buf, size); /* leave some stack space - 256 bytes for now XXX -BL */ v_addii(sp, sp, -512); /* Original comments follow: */ /* XXX - This is just here to support the boot */ /* Now it actually supports the memory system also */ /* void ReenterTC_CX( EmbraState* ) */ /* The jumpPC better be a C function or a standard TC entry point. */ /* For it to be a (more) arbitrary entry point, we need the scheme*/ /* discussed in phys_mem_ref_wrapper in qc.c */ /* XXX This thing may have to be changed!! */ v_movu( vss_base, arg[0]); /* initialize vss_base with curEmp */ /* Load current countdown b/c it was adjusted */ v_ldui( clock_reg, vss_base, CCD_OFF); v_ldui( s2, vss_base, SSREG2_OFF); REG_LD_OP( pc_reg, vss_base, PC_OFF); v_ldui( dhit_reg, vss_base, SDHIT_OFF); v_ldui( ihit_reg, vss_base, SIHIT_OFF); /* This is really only necessary for debugging */ /* it also probably hoses us somehow!! */ v_stui( ra, sp, STOFF_RA); /* This gets stored as the jumpPC for this CPU */ v_ldui( ra, vss_base, JUMPPC_OFF); ASSERT(Embra_CX != 0); v_jpi( Embra_CX); v_nop(); return v_end(used).v;}vfptr GenIncCCasm(char *buf, int size, int *used){ /* ack! we wanted to save our calling conventions, * but I think that might have been hosing us because * the original code stores ra in STOFF_RA */ v_lambda("GeneralEmEventPollasm", "", NULL, V_LEAF, (v_code *) buf, size); v_stui(ra, sp, STOFF_RA); /* call C routine to increment (long) cycle count */ v_ldui(a0, vss_base, MYNUM_OFF); v_seti(t9, (unsigned) IncCC); v_jalp(ra, t9); /* was: v_scallv(IncCC, "%u", a0); */ /* get low word of cycle count */ v_ldui(v0, vss_base, LOCC_OFF); v_ldui(ra, sp, STOFF_RA); /* jr ra */ return v_end(used).v;}vfptr GenGeneralEmEventPollasm(char *buf, int size, int *used){ v_lambda("GeneralEmEventPollasm", "", NULL, V_NLEAF, (v_code *) buf, size); /* This procedure DOES NOT return */ v_ldui(a0, vss_base, MYNUM_OFF); v_scallv(GeneralEmEventPoll, "%u", a0); /* This procedure raises an assert of the above procedure returns */ v_scallv(NonReturningProcReturned, ""); return v_end(used).v;}/* EmEventPoll: Check for pending events and service if necessary. */vfptr GenEmEventPoll(char *buf, int size, int *used){ static v_label_t GeneralEmEventPollasm_label; /* has to be static? */ static v_label_t restore_proc_label; GeneralEmEventPollasm_label = v_genlabel(); restore_proc_label = v_genlabel(); /* I didn't think up this name */ v_lambda("EmEventPoll", "", NULL, V_LEAF, (v_code *) buf, size); /* Load callback time ptr */ v_ldui(t0, vss_base, CALL_OFF); /* VSS_BASE = EMP[numCPUs].next -- Don't need old VSS_BASE anymore */ v_ldui(vss_base, vss_base, NEXT_OFF); /* Old 3 instruction sequence to keep track of current cpu... la t1, curr_cpu lw t2, MYNUM_OFF(VSS_BASE) sw t2, 0(t1) */ /* New 2 insn sequence */ { v_seti(t1, ((unsigned)&curEmp) & 0xffff0000); /* XXX this is dumb - fix?? */ } v_orui(t1, t1, ((unsigned)&curEmp) & 0x0000ffff); v_stui(vss_base, t1, 0); /* Load hi end of callback time */ v_ldui(t1, t0, 0); /* Load hi end of cycle count */ v_ldui(t2, vss_base, HICC_OFF); /* If the most signifigant word of the two do not agree, bail */ /* GeneralEvEventPollasm does not return */ v_bnei(t1, t2, GeneralEmEventPollasm_label); /* Load lo end of callback time */ v_ldui(t1, t0, 4); /* Load lo end of cycle count */ v_ldui(t2, vss_base, LOCC_OFF); /* t3 = cyclecount - callbacktime */ v_subu(t3, t2, t1); /* Event is probably pending -- service it */ /* if (t3 >=0) jp GeneralEventPollasm (does not return) */ v_bltii(t3, 0, restore_proc_label); /* GeneralEmEventPollasm_label: */ v_label(GeneralEmEventPollasm_label); ASSERT(GeneralEmEventPollasm != 0); v_jpi(GeneralEmEventPollasm); v_label(restore_proc_label); /* Load S registers */ LOAD_COMMON_VARS v_ldui(clock_reg, vss_base, CCD_OFF); v_ldui(dhit_reg, vss_base, SDHIT_OFF); v_ldui(ihit_reg, vss_base, SIHIT_OFF); REG_LD_OP(pc_reg, vss_base, PC_OFF); v_ldui(t9, vss_base, JUMPPC_OFF); ASSERT(Embra_CX_nochain != 0); v_jpi(Embra_CX_nochain); v_nop(); return v_end(used).v;} /* GenEmEventPoll */ vfptr GenUPperiodic_callout(char *buf, int size, int *used){ static v_label_t up_event; v_lambda("UPperiodic)callout", "", NULL, V_LEAF, (v_code *) buf, size); up_event = v_genlabel(); /* original comments follow: */ /* XXX - This is the WRONG solution. The right way of doing things */ /* is to figure out why the number of interrupts with and without */ /* polling is different. Because we check for interrupts when we */ /* deliver them and when the SR is changed, we shouldn't need to poll */ /* Someone needs to route around in clock.c */ /* This is a fast way of checking to see if any callbacks are pending.*/ /* The C routine is high on the profile */ REG_ST_OP(pc_reg, vss_base, PC_OFF); v_stui(ra, vss_base, JUMPPC_OFF); /* Increment cycle count (int64) */ v_seti(a0, ((unsigned)&callout_interval)); /* get callout interval */ v_ldui(a0, a0, 0); v_ldui(sim_t1, vss_base, LOCC_OFF); v_ldui(sim_t2, vss_base, HICC_OFF); v_addu(sim_t4, sim_t1, a0); v_ltu(sim_t1, sim_t4, sim_t1); v_addu(sim_t2, sim_t2, sim_t1); v_stui(sim_t4, vss_base, LOCC_OFF); v_stui(sim_t2, vss_base, HICC_OFF); /* increment CLOCK register */ v_addu(clock_reg, clock_reg, a0); v_stui(clock_reg, vss_base, CCD_OFF); /* load callback time ptr */ v_ldui(a0, vss_base, CALL_OFF); /* compare hi */ v_ldui(a1, a0, 0); v_bneu(a1, sim_t2, up_event); v_ldui(a1, a0, 4); v_bltu(a1, sim_t4, up_event); /* return */ v_jp(ra); v_label(up_event); SPILL_FP_IF_ENABLED CORRECT_OUTGOING_CC(zero); v_setl(t9, ((unsigned) CEmEventPoll)); v_jalp(ra,t9);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -