added various arm modules from skyeye to make project link OK
This commit is contained in:
parent
20807c4d5a
commit
6b255111d5
@ -139,9 +139,14 @@
|
||||
<ItemGroup>
|
||||
<ClCompile Include="src\arm\armemu.cpp" />
|
||||
<ClCompile Include="src\arm\arminit.cpp" />
|
||||
<ClCompile Include="src\arm\armmmu.cpp" />
|
||||
<ClCompile Include="src\arm\armos.cpp" />
|
||||
<ClCompile Include="src\arm\armsupp.cpp" />
|
||||
<ClCompile Include="src\arm\armvirt.cpp" />
|
||||
<ClCompile Include="src\arm\disassembler\arm_disasm.cpp" />
|
||||
<ClCompile Include="src\core.cpp" />
|
||||
<ClCompile Include="src\core_timing.cpp" />
|
||||
<ClCompile Include="src\elf\elf_reader.cpp" />
|
||||
<ClCompile Include="src\file_sys\directory_file_system.cpp" />
|
||||
<ClCompile Include="src\file_sys\meta_file_system.cpp" />
|
||||
<ClCompile Include="src\loader.cpp" />
|
||||
@ -165,7 +170,8 @@
|
||||
<ClInclude Include="src\arm\skyeye_defs.h" />
|
||||
<ClInclude Include="src\core.h" />
|
||||
<ClInclude Include="src\core_timing.h" />
|
||||
<ClInclude Include="src\elf\elf.h" />
|
||||
<ClInclude Include="src\elf\elf_reader.h" />
|
||||
<ClInclude Include="src\elf\elf_types.h" />
|
||||
<ClInclude Include="src\file_sys\directory_file_system.h" />
|
||||
<ClInclude Include="src\file_sys\file_sys.h" />
|
||||
<ClInclude Include="src\file_sys\meta_file_system.h" />
|
||||
|
@ -22,6 +22,21 @@
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\system.cpp" />
|
||||
<ClCompile Include="src\core_timing.cpp" />
|
||||
<ClCompile Include="src\elf\elf_reader.cpp">
|
||||
<Filter>elf</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\arm\armsupp.cpp">
|
||||
<Filter>arm</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\arm\armvirt.cpp">
|
||||
<Filter>arm</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\arm\armmmu.cpp">
|
||||
<Filter>arm</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="src\arm\armos.cpp">
|
||||
<Filter>arm</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="arm">
|
||||
@ -94,7 +109,10 @@
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\system.h" />
|
||||
<ClInclude Include="src\core_timing.h" />
|
||||
<ClInclude Include="src\elf\elf.h">
|
||||
<ClInclude Include="src\elf\elf_reader.h">
|
||||
<Filter>elf</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="src\elf\elf_types.h">
|
||||
<Filter>elf</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
|
@ -21,6 +21,19 @@
|
||||
#include "armemu.h"
|
||||
#include "armos.h"
|
||||
|
||||
void
|
||||
XScale_set_fsr_far(ARMul_State * state, ARMword fsr, ARMword _far)
|
||||
{
|
||||
_dbg_assert_msg_(ARM11, false, "ImplementMe: XScale_set_fsr_far!");
|
||||
//if (!state->is_XScale || (read_cp14_reg(10) & (1UL << 31)) == 0)
|
||||
// return;
|
||||
//
|
||||
//write_cp15_reg(state, 5, 0, 0, fsr);
|
||||
//write_cp15_reg(state, 6, 0, 0, _far);
|
||||
}
|
||||
|
||||
#define ARMul_Debug(x,y,z) 0 // Disabling this /bunnei
|
||||
|
||||
//#include "skyeye_callback.h"
|
||||
//#include "skyeye_bus.h"
|
||||
//#include "sim_control.h"
|
||||
@ -2174,10 +2187,10 @@ ARMul_Emulate26 (ARMul_State * state)
|
||||
(state,
|
||||
ARMul_CP15_R5_MMU_EXCPT,
|
||||
pc);
|
||||
if (!XScale_debug_moe
|
||||
(state,
|
||||
ARMul_CP14_R10_MOE_BT))
|
||||
break;
|
||||
//if (!XScale_debug_moe
|
||||
// (state,
|
||||
// ARMul_CP14_R10_MOE_BT))
|
||||
// break; // Disabled /bunnei
|
||||
}
|
||||
|
||||
/* Force the next instruction to be refetched. */
|
||||
|
@ -73,6 +73,7 @@ extern ARMword isize;
|
||||
#define ASSIGNT(res) state->TFlag = res
|
||||
#define INSN_SIZE (TFLAG ? 2 : 4)
|
||||
#else
|
||||
#define TBIT (1L << 5)
|
||||
#define INSN_SIZE 4
|
||||
#define TFLAG 0
|
||||
#endif
|
||||
@ -229,12 +230,12 @@ extern ARMword isize;
|
||||
} \
|
||||
while (0)
|
||||
|
||||
#ifndef MODE32
|
||||
//#ifndef MODE32
|
||||
#define VECTORS 0x20
|
||||
#define LEGALADDR 0x03ffffff
|
||||
#define VECTORACCESS(address) (address < VECTORS && ARMul_MODE26BIT && state->prog32Sig)
|
||||
#define ADDREXCEPT(address) (address > LEGALADDR && !state->data32Sig)
|
||||
#endif
|
||||
//#endif
|
||||
|
||||
#define INTERNALABORT(address) \
|
||||
do \
|
||||
@ -409,10 +410,12 @@ extern ARMword isize;
|
||||
|| (! (STATE)->is_XScale) \
|
||||
|| (read_cp15_reg (15, 0, 1) & (1 << (CP))))
|
||||
*/
|
||||
#define CP_ACCESS_ALLOWED(STATE, CP) \
|
||||
( ((CP) >= 14) \
|
||||
|| (! (STATE)->is_XScale) \
|
||||
|| (xscale_cp15_cp_access_allowed(STATE,15,CP)))
|
||||
//#define CP_ACCESS_ALLOWED(STATE, CP) \
|
||||
// (((CP) >= 14) \
|
||||
// || (!(STATE)->is_XScale) \
|
||||
// || (xscale_cp15_cp_access_allowed(STATE, 15, CP)))
|
||||
|
||||
#define CP_ACCESS_ALLOWED(STATE, CP) false // Disabled coprocessor shit /bunnei
|
||||
|
||||
/* Macro to rotate n right by b bits. */
|
||||
#define ROTATER(n, b) (((n) >> (b)) | ((n) << (32 - (b))))
|
||||
|
@ -271,7 +271,7 @@ below line sould be in skyeye_mach_XXX.c 's XXX_mach_init function
|
||||
|
||||
/* Only initialse the coprocessor support once we
|
||||
know what kind of chip we are dealing with. */
|
||||
ARMul_CoProInit (state);
|
||||
//ARMul_CoProInit (state); Commented out /bunnei
|
||||
|
||||
}
|
||||
|
||||
@ -318,7 +318,7 @@ ARMul_Reset (ARMul_State * state)
|
||||
state->NumFcycles = 0;
|
||||
|
||||
//fprintf(stderr,"armul_reset 3: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
|
||||
mmu_reset (state);
|
||||
//mmu_reset (state); Commented out /bunnei
|
||||
//fprintf(stderr,"armul_reset 4: state-> Cpsr 0x%x, Mode %d\n",state->Cpsr,state->Mode);
|
||||
|
||||
//mem_reset (state); /* move to memory/ram.c */
|
||||
@ -436,7 +436,8 @@ ARMul_DoProg (ARMul_State * state)
|
||||
}
|
||||
|
||||
else {
|
||||
pc = ARMul_Emulate26 (state);
|
||||
//pc = ARMul_Emulate26 (state); Commented out /bunnei
|
||||
ERROR_LOG(ARM11, "Unsupported ARM 26-bit Mode!");
|
||||
}
|
||||
//chy 2006-02-22, should test debugmode first
|
||||
//chy 2006-04-14, put below codes in ARMul_Emulate
|
||||
@ -489,8 +490,10 @@ ARMul_DoInstr (ARMul_State * state)
|
||||
#endif
|
||||
}
|
||||
|
||||
else
|
||||
pc = ARMul_Emulate26 (state);
|
||||
else {
|
||||
//pc = ARMul_Emulate26 (state); Commented out /bunnei
|
||||
ERROR_LOG(ARM11, "Unsupported ARM 26-bit Mode!");
|
||||
}
|
||||
|
||||
return (pc);
|
||||
}
|
||||
@ -501,78 +504,78 @@ ARMul_DoInstr (ARMul_State * state)
|
||||
* appropriate vector's memory address (0,4,8 ....) *
|
||||
\***************************************************************************/
|
||||
|
||||
//void
|
||||
//ARMul_Abort (ARMul_State * state, ARMword vector)
|
||||
//{
|
||||
// ARMword temp;
|
||||
// int isize = INSN_SIZE;
|
||||
// int esize = (TFLAG ? 0 : 4);
|
||||
// int e2size = (TFLAG ? -4 : 0);
|
||||
//
|
||||
// state->Aborted = FALSE;
|
||||
//
|
||||
// if (state->prog32Sig)
|
||||
// if (ARMul_MODE26BIT)
|
||||
// temp = R15PC;
|
||||
// else
|
||||
// temp = state->Reg[15];
|
||||
// else
|
||||
// temp = R15PC | ECC | ER15INT | EMODE;
|
||||
//
|
||||
// switch (vector) {
|
||||
// case ARMul_ResetV: /* RESET */
|
||||
// SETABORT (INTBITS, state->prog32Sig ? SVC32MODE : SVC26MODE,
|
||||
// 0);
|
||||
// break;
|
||||
// case ARMul_UndefinedInstrV: /* Undefined Instruction */
|
||||
// SETABORT (IBIT, state->prog32Sig ? UNDEF32MODE : SVC26MODE,
|
||||
// isize);
|
||||
// break;
|
||||
// case ARMul_SWIV: /* Software Interrupt */
|
||||
// SETABORT (IBIT, state->prog32Sig ? SVC32MODE : SVC26MODE,
|
||||
// isize);
|
||||
// break;
|
||||
// case ARMul_PrefetchAbortV: /* Prefetch Abort */
|
||||
// state->AbortAddr = 1;
|
||||
// SETABORT (IBIT, state->prog32Sig ? ABORT32MODE : SVC26MODE,
|
||||
// esize);
|
||||
// break;
|
||||
// case ARMul_DataAbortV: /* Data Abort */
|
||||
// SETABORT (IBIT, state->prog32Sig ? ABORT32MODE : SVC26MODE,
|
||||
// e2size);
|
||||
// break;
|
||||
// case ARMul_AddrExceptnV: /* Address Exception */
|
||||
// SETABORT (IBIT, SVC26MODE, isize);
|
||||
// break;
|
||||
// case ARMul_IRQV: /* IRQ */
|
||||
// //chy 2003-09-02 the if sentence seems no use
|
||||
//#if 0
|
||||
// if (!state->is_XScale || !state->CPRead[13] (state, 0, &temp)
|
||||
// || (temp & ARMul_CP13_R0_IRQ))
|
||||
//#endif
|
||||
// SETABORT (IBIT,
|
||||
// state->prog32Sig ? IRQ32MODE : IRQ26MODE,
|
||||
// esize);
|
||||
// break;
|
||||
// case ARMul_FIQV: /* FIQ */
|
||||
// //chy 2003-09-02 the if sentence seems no use
|
||||
//#if 0
|
||||
// if (!state->is_XScale || !state->CPRead[13] (state, 0, &temp)
|
||||
// || (temp & ARMul_CP13_R0_FIQ))
|
||||
//#endif
|
||||
// SETABORT (INTBITS,
|
||||
// state->prog32Sig ? FIQ32MODE : FIQ26MODE,
|
||||
// esize);
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// if (ARMul_MODE32BIT) {
|
||||
// if (state->mmu.control & CONTROL_VECTOR)
|
||||
// vector += 0xffff0000; //for v4 high exception address
|
||||
// if (state->vector_remap_flag)
|
||||
// vector += state->vector_remap_addr; /* support some remap function in LPC processor */
|
||||
// ARMul_SetR15 (state, vector);
|
||||
// }
|
||||
// else
|
||||
// ARMul_SetR15 (state, R15CCINTMODE | vector);
|
||||
//}
|
||||
void
|
||||
ARMul_Abort (ARMul_State * state, ARMword vector)
|
||||
{
|
||||
ARMword temp;
|
||||
int isize = INSN_SIZE;
|
||||
int esize = (TFLAG ? 0 : 4);
|
||||
int e2size = (TFLAG ? -4 : 0);
|
||||
|
||||
state->Aborted = FALSE;
|
||||
|
||||
if (state->prog32Sig)
|
||||
if (ARMul_MODE26BIT)
|
||||
temp = R15PC;
|
||||
else
|
||||
temp = state->Reg[15];
|
||||
else
|
||||
temp = R15PC | ECC | ER15INT | EMODE;
|
||||
|
||||
switch (vector) {
|
||||
case ARMul_ResetV: /* RESET */
|
||||
SETABORT (INTBITS, state->prog32Sig ? SVC32MODE : SVC26MODE,
|
||||
0);
|
||||
break;
|
||||
case ARMul_UndefinedInstrV: /* Undefined Instruction */
|
||||
SETABORT (IBIT, state->prog32Sig ? UNDEF32MODE : SVC26MODE,
|
||||
isize);
|
||||
break;
|
||||
case ARMul_SWIV: /* Software Interrupt */
|
||||
SETABORT (IBIT, state->prog32Sig ? SVC32MODE : SVC26MODE,
|
||||
isize);
|
||||
break;
|
||||
case ARMul_PrefetchAbortV: /* Prefetch Abort */
|
||||
state->AbortAddr = 1;
|
||||
SETABORT (IBIT, state->prog32Sig ? ABORT32MODE : SVC26MODE,
|
||||
esize);
|
||||
break;
|
||||
case ARMul_DataAbortV: /* Data Abort */
|
||||
SETABORT (IBIT, state->prog32Sig ? ABORT32MODE : SVC26MODE,
|
||||
e2size);
|
||||
break;
|
||||
case ARMul_AddrExceptnV: /* Address Exception */
|
||||
SETABORT (IBIT, SVC26MODE, isize);
|
||||
break;
|
||||
case ARMul_IRQV: /* IRQ */
|
||||
//chy 2003-09-02 the if sentence seems no use
|
||||
#if 0
|
||||
if (!state->is_XScale || !state->CPRead[13] (state, 0, &temp)
|
||||
|| (temp & ARMul_CP13_R0_IRQ))
|
||||
#endif
|
||||
SETABORT (IBIT,
|
||||
state->prog32Sig ? IRQ32MODE : IRQ26MODE,
|
||||
esize);
|
||||
break;
|
||||
case ARMul_FIQV: /* FIQ */
|
||||
//chy 2003-09-02 the if sentence seems no use
|
||||
#if 0
|
||||
if (!state->is_XScale || !state->CPRead[13] (state, 0, &temp)
|
||||
|| (temp & ARMul_CP13_R0_FIQ))
|
||||
#endif
|
||||
SETABORT (INTBITS,
|
||||
state->prog32Sig ? FIQ32MODE : FIQ26MODE,
|
||||
esize);
|
||||
break;
|
||||
}
|
||||
|
||||
if (ARMul_MODE32BIT) {
|
||||
if (state->mmu.control & CONTROL_VECTOR)
|
||||
vector += 0xffff0000; //for v4 high exception address
|
||||
if (state->vector_remap_flag)
|
||||
vector += state->vector_remap_addr; /* support some remap function in LPC processor */
|
||||
ARMul_SetR15 (state, vector);
|
||||
}
|
||||
else
|
||||
ARMul_SetR15 (state, R15CCINTMODE | vector);
|
||||
}
|
||||
|
@ -45,55 +45,51 @@ mmu_init (ARMul_State * state)
|
||||
state->mmu.process_id = 0;
|
||||
|
||||
switch (state->cpu->cpu_val & state->cpu->cpu_mask) {
|
||||
case SA1100:
|
||||
case SA1110:
|
||||
SKYEYE_INFO("SKYEYE: use sa11xx mmu ops\n");
|
||||
state->mmu.ops = sa_mmu_ops;
|
||||
break;
|
||||
case PXA250:
|
||||
case PXA270: //xscale
|
||||
SKYEYE_INFO ("SKYEYE: use xscale mmu ops\n");
|
||||
state->mmu.ops = xscale_mmu_ops;
|
||||
break;
|
||||
case 0x41807200: //arm720t
|
||||
case 0x41007700: //arm7tdmi
|
||||
case 0x41007100: //arm7100
|
||||
SKYEYE_INFO ( "SKYEYE: use arm7100 mmu ops\n");
|
||||
state->mmu.ops = arm7100_mmu_ops;
|
||||
break;
|
||||
case 0x41009200:
|
||||
SKYEYE_INFO ("SKYEYE: use arm920t mmu ops\n");
|
||||
state->mmu.ops = arm920t_mmu_ops;
|
||||
break;
|
||||
case 0x41069260:
|
||||
SKYEYE_INFO ("SKYEYE: use arm926ejs mmu ops\n");
|
||||
state->mmu.ops = arm926ejs_mmu_ops;
|
||||
break;
|
||||
//case SA1100:
|
||||
//case SA1110:
|
||||
// NOTICE_LOG(ARM11, "SKYEYE: use sa11xx mmu ops\n");
|
||||
// state->mmu.ops = sa_mmu_ops;
|
||||
// break;
|
||||
//case PXA250:
|
||||
//case PXA270: //xscale
|
||||
// NOTICE_LOG(ARM11, "SKYEYE: use xscale mmu ops\n");
|
||||
// state->mmu.ops = xscale_mmu_ops;
|
||||
// break;
|
||||
//case 0x41807200: //arm720t
|
||||
//case 0x41007700: //arm7tdmi
|
||||
//case 0x41007100: //arm7100
|
||||
// NOTICE_LOG(ARM11, "SKYEYE: use arm7100 mmu ops\n");
|
||||
// state->mmu.ops = arm7100_mmu_ops;
|
||||
// break;
|
||||
//case 0x41009200:
|
||||
// NOTICE_LOG(ARM11, "SKYEYE: use arm920t mmu ops\n");
|
||||
// state->mmu.ops = arm920t_mmu_ops;
|
||||
// break;
|
||||
//case 0x41069260:
|
||||
// NOTICE_LOG(ARM11, "SKYEYE: use arm926ejs mmu ops\n");
|
||||
// state->mmu.ops = arm926ejs_mmu_ops;
|
||||
// break;
|
||||
/* case 0x560f5810: */
|
||||
case 0x0007b000:
|
||||
SKYEYE_INFO ("SKYEYE: use arm11jzf-s mmu ops\n");
|
||||
state->mmu.ops = arm1176jzf_s_mmu_ops;
|
||||
NOTICE_LOG(ARM11, "SKYEYE: use arm11jzf-s mmu ops\n");
|
||||
//state->mmu.ops = arm1176jzf_s_mmu_ops;
|
||||
_dbg_assert_msg_(ARM11, false, "ImplementMe: arm1176jzf_s_mmu_ops!");
|
||||
break;
|
||||
|
||||
case 0xc090:
|
||||
SKYEYE_INFO ("SKYEYE: use cortex_a9 mmu ops\n");
|
||||
state->mmu.ops = cortex_a9_mmu_ops;
|
||||
break;
|
||||
default:
|
||||
fprintf (stderr,
|
||||
ERROR_LOG (ARM11,
|
||||
"SKYEYE: armmmu.c : mmu_init: unknown cpu_val&cpu_mask 0x%x\n",
|
||||
state->cpu->cpu_val & state->cpu->cpu_mask);
|
||||
skyeye_exit (-1);
|
||||
break;
|
||||
|
||||
};
|
||||
ret = state->mmu.ops.init (state);
|
||||
state->mmu_inited = (ret == 0);
|
||||
/* initialize mmu_read and mmu_write for disassemble */
|
||||
skyeye_config_t *config = get_current_config();
|
||||
generic_arch_t *arch_instance = get_arch_instance(config->arch->arch_name);
|
||||
arch_instance->mmu_read = arm_mmu_read;
|
||||
arch_instance->mmu_write = arm_mmu_write;
|
||||
//skyeye_config_t *config = get_current_config();
|
||||
//generic_arch_t *arch_instance = get_arch_instance(config->arch->arch_name);
|
||||
//arch_instance->mmu_read = arm_mmu_read;
|
||||
//arch_instance->mmu_write = arm_mmu_write;
|
||||
|
||||
return ret;
|
||||
}
|
||||
@ -201,41 +197,43 @@ mmu_v2p_dbct (ARMul_State * state, ARMword virt_addr, ARMword * phys_addr)
|
||||
return (MMU_OPS.v2p_dbct (state, virt_addr, phys_addr));
|
||||
}
|
||||
|
||||
/* dis_mmu_read for disassemble */
|
||||
exception_t arm_mmu_read(short size, generic_address_t addr, uint32_t * value)
|
||||
{
|
||||
ARMul_State *state;
|
||||
ARM_CPU_State *cpu = get_current_cpu();
|
||||
state = &cpu->core[0];
|
||||
switch(size){
|
||||
case 8:
|
||||
MMU_OPS.read_byte (state, addr, value);
|
||||
break;
|
||||
case 16:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
printf("In %s error size %d Line %d\n", __func__, size, __LINE__);
|
||||
break;
|
||||
}
|
||||
return No_exp;
|
||||
}
|
||||
/* dis_mmu_write for disassemble */
|
||||
exception_t arm_mmu_write(short size, generic_address_t addr, uint32_t *value)
|
||||
{
|
||||
ARMul_State *state;
|
||||
ARM_CPU_State *cpu = get_current_cpu();
|
||||
state = &cpu->core[0];
|
||||
switch(size){
|
||||
case 8:
|
||||
MMU_OPS.write_byte (state, addr, value);
|
||||
break;
|
||||
case 16:
|
||||
case 32:
|
||||
break;
|
||||
default:
|
||||
printf("In %s error size %d Line %d\n", __func__, size, __LINE__);
|
||||
break;
|
||||
}
|
||||
return No_exp;
|
||||
}
|
||||
//
|
||||
//
|
||||
///* dis_mmu_read for disassemble */
|
||||
//exception_t arm_mmu_read(short size, uint32_t addr, uint32_t * value)
|
||||
//{
|
||||
// ARMul_State *state;
|
||||
// ARM_CPU_State *cpu = get_current_cpu();
|
||||
// state = &cpu->core[0];
|
||||
// switch(size){
|
||||
// case 8:
|
||||
// MMU_OPS.read_byte (state, addr, value);
|
||||
// break;
|
||||
// case 16:
|
||||
// case 32:
|
||||
// break;
|
||||
// default:
|
||||
// ERROR_LOG(ARM11, "Error size %d", size);
|
||||
// break;
|
||||
// }
|
||||
// return No_exp;
|
||||
//}
|
||||
///* dis_mmu_write for disassemble */
|
||||
//exception_t arm_mmu_write(short size, uint32_t addr, uint32_t *value)
|
||||
//{
|
||||
// ARMul_State *state;
|
||||
// ARM_CPU_State *cpu = get_current_cpu();
|
||||
// state = &cpu->core[0];
|
||||
// switch(size){
|
||||
// case 8:
|
||||
// MMU_OPS.write_byte (state, addr, value);
|
||||
// break;
|
||||
// case 16:
|
||||
// case 32:
|
||||
// break;
|
||||
// default:
|
||||
// printf("In %s error size %d Line %d\n", __func__, size, __LINE__);
|
||||
// break;
|
||||
// }
|
||||
// return No_exp;
|
||||
//}
|
||||
|
742
src/core/src/arm/armos.cpp
Normal file
742
src/core/src/arm/armos.cpp
Normal file
@ -0,0 +1,742 @@
|
||||
/* armos.c -- ARMulator OS interface: ARM6 Instruction Emulator.
|
||||
Copyright (C) 1994 Advanced RISC Machines Ltd.
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* This file contains a model of Demon, ARM Ltd's Debug Monitor,
|
||||
including all the SWI's required to support the C library. The code in
|
||||
it is not really for the faint-hearted (especially the abort handling
|
||||
code), but it is a complete example. Defining NOOS will disable all the
|
||||
fun, and definign VAILDATE will define SWI 1 to enter SVC mode, and SWI
|
||||
0x11 to halt the emulator. */
|
||||
|
||||
//chy 2005-09-12 disable below line
|
||||
//#include "config.h"
|
||||
|
||||
#include <time.h>
|
||||
#include <errno.h>
|
||||
#include <string.h>
|
||||
#include "skyeye_defs.h"
|
||||
#ifndef __USE_LARGEFILE64
|
||||
#define __USE_LARGEFILE64 /* When use 64 bit large file need define it! for stat64*/
|
||||
#endif
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
|
||||
#ifndef O_RDONLY
|
||||
#define O_RDONLY 0
|
||||
#endif
|
||||
#ifndef O_WRONLY
|
||||
#define O_WRONLY 1
|
||||
#endif
|
||||
#ifndef O_RDWR
|
||||
#define O_RDWR 2
|
||||
#endif
|
||||
#ifndef O_BINARY
|
||||
#define O_BINARY 0
|
||||
#endif
|
||||
|
||||
#ifdef __STDC__
|
||||
#define unlink(s) remove(s)
|
||||
#endif
|
||||
|
||||
#ifdef HAVE_UNISTD_H
|
||||
#include <unistd.h> /* For SEEK_SET etc */
|
||||
#endif
|
||||
|
||||
#ifdef __riscos
|
||||
extern int _fisatty (FILE *);
|
||||
#define isatty_(f) _fisatty(f)
|
||||
#else
|
||||
#ifdef __ZTC__
|
||||
#include <io.h>
|
||||
#define isatty_(f) isatty((f)->_file)
|
||||
#else
|
||||
#ifdef macintosh
|
||||
#include <ioctl.h>
|
||||
#define isatty_(f) (~ioctl ((f)->_file, FIOINTERACTIVE, NULL))
|
||||
#else
|
||||
#define isatty_(f) isatty (fileno (f))
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#include "armdefs.h"
|
||||
#include "armos.h"
|
||||
#include "armemu.h"
|
||||
|
||||
#ifndef NOOS
|
||||
#ifndef VALIDATE
|
||||
/* #ifndef ASIM */
|
||||
//chy 2005-09-12 disable below line
|
||||
//#include "armfpe.h"
|
||||
/* #endif */
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#define DUMP_SYSCALL 0
|
||||
#define dump(...) do { if (DUMP_SYSCALL) printf(__VA_ARGS__); } while(0)
|
||||
//#define debug(...) printf(__VA_ARGS__);
|
||||
#define debug(...) ;
|
||||
|
||||
extern unsigned ARMul_OSHandleSWI (ARMul_State * state, ARMword number);
|
||||
|
||||
#ifndef FOPEN_MAX
|
||||
#define FOPEN_MAX 64
|
||||
#endif
|
||||
|
||||
/***************************************************************************\
|
||||
* OS private Information *
|
||||
\***************************************************************************/
|
||||
|
||||
unsigned arm_dyncom_SWI(ARMul_State * state, ARMword number)
|
||||
{
|
||||
return ARMul_OSHandleSWI(state, number);
|
||||
}
|
||||
|
||||
//mmap_area_t *mmap_global = NULL;
|
||||
|
||||
static int translate_open_mode[] = {
|
||||
O_RDONLY, /* "r" */
|
||||
O_RDONLY + O_BINARY, /* "rb" */
|
||||
O_RDWR, /* "r+" */
|
||||
O_RDWR + O_BINARY, /* "r+b" */
|
||||
O_WRONLY + O_CREAT + O_TRUNC, /* "w" */
|
||||
O_WRONLY + O_BINARY + O_CREAT + O_TRUNC, /* "wb" */
|
||||
O_RDWR + O_CREAT + O_TRUNC, /* "w+" */
|
||||
O_RDWR + O_BINARY + O_CREAT + O_TRUNC, /* "w+b" */
|
||||
O_WRONLY + O_APPEND + O_CREAT, /* "a" */
|
||||
O_WRONLY + O_BINARY + O_APPEND + O_CREAT, /* "ab" */
|
||||
O_RDWR + O_APPEND + O_CREAT, /* "a+" */
|
||||
O_RDWR + O_BINARY + O_APPEND + O_CREAT /* "a+b" */
|
||||
};
|
||||
//
|
||||
//static void
|
||||
//SWIWrite0 (ARMul_State * state, ARMword addr)
|
||||
//{
|
||||
// ARMword temp;
|
||||
//
|
||||
// //while ((temp = ARMul_ReadByte (state, addr++)) != 0)
|
||||
// while(1){
|
||||
// mem_read(8, addr++, &temp);
|
||||
// if(temp != 0)
|
||||
// (void) fputc ((char) temp, stdout);
|
||||
// else
|
||||
// break;
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static void
|
||||
//WriteCommandLineTo (ARMul_State * state, ARMword addr)
|
||||
//{
|
||||
// ARMword temp;
|
||||
// char *cptr = state->CommandLine;
|
||||
// if (cptr == NULL)
|
||||
// cptr = "\0";
|
||||
// do {
|
||||
// temp = (ARMword) * cptr++;
|
||||
// //ARMul_WriteByte (state, addr++, temp);
|
||||
// mem_write(8, addr++, temp);
|
||||
// }
|
||||
// while (temp != 0);
|
||||
//}
|
||||
//
|
||||
//static void
|
||||
//SWIopen (ARMul_State * state, ARMword name, ARMword SWIflags)
|
||||
//{
|
||||
// char dummy[2000];
|
||||
// int flags;
|
||||
// int i;
|
||||
//
|
||||
// for (i = 0; (dummy[i] = ARMul_ReadByte (state, name + i)); i++);
|
||||
// assert(SWIflags< (sizeof(translate_open_mode)/ sizeof(translate_open_mode[0])));
|
||||
// /* Now we need to decode the Demon open mode */
|
||||
// flags = translate_open_mode[SWIflags];
|
||||
// flags = SWIflags;
|
||||
//
|
||||
// /* Filename ":tt" is special: it denotes stdin/out */
|
||||
// if (strcmp (dummy, ":tt") == 0) {
|
||||
// if (flags == O_RDONLY) /* opening tty "r" */
|
||||
// state->Reg[0] = 0; /* stdin */
|
||||
// else
|
||||
// state->Reg[0] = 1; /* stdout */
|
||||
// }
|
||||
// else {
|
||||
// state->Reg[0] = (int) open (dummy, flags, 0666);
|
||||
// }
|
||||
//}
|
||||
//
|
||||
//static void
|
||||
//SWIread (ARMul_State * state, ARMword f, ARMword ptr, ARMword len)
|
||||
//{
|
||||
// int res;
|
||||
// int i;
|
||||
// char *local = (char*) malloc (len);
|
||||
//
|
||||
// if (local == NULL) {
|
||||
// fprintf (stderr,
|
||||
// "sim: Unable to read 0x%ulx bytes - out of memory\n",
|
||||
// len);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// res = read (f, local, len);
|
||||
// if (res > 0)
|
||||
// for (i = 0; i < res; i++)
|
||||
// //ARMul_WriteByte (state, ptr + i, local[i]);
|
||||
// mem_write(8, ptr + i, local[i]);
|
||||
// free (local);
|
||||
// //state->Reg[0] = res == -1 ? -1 : len - res;
|
||||
// state->Reg[0] = res;
|
||||
//}
|
||||
//
|
||||
//static void
|
||||
//SWIwrite (ARMul_State * state, ARMword f, ARMword ptr, ARMword len)
|
||||
//{
|
||||
// int res;
|
||||
// ARMword i;
|
||||
// char *local = malloc (len);
|
||||
//
|
||||
// if (local == NULL) {
|
||||
// fprintf (stderr,
|
||||
// "sim: Unable to write 0x%lx bytes - out of memory\n",
|
||||
// (long unsigned int) len);
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// for (i = 0; i < len; i++){
|
||||
// //local[i] = ARMul_ReadByte (state, ptr + i);
|
||||
// ARMword data;
|
||||
// mem_read(8, ptr + i, &data);
|
||||
// local[i] = data & 0xFF;
|
||||
// }
|
||||
//
|
||||
// res = write (f, local, len);
|
||||
// //state->Reg[0] = res == -1 ? -1 : len - res;
|
||||
// state->Reg[0] = res;
|
||||
// free (local);
|
||||
//}
|
||||
|
||||
//static void
|
||||
//SWIflen (ARMul_State * state, ARMword fh)
|
||||
//{
|
||||
// ARMword addr;
|
||||
//
|
||||
// if (fh == 0 || fh > FOPEN_MAX) {
|
||||
// state->Reg[0] = -1L;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// addr = lseek (fh, 0, SEEK_CUR);
|
||||
//
|
||||
// state->Reg[0] = lseek (fh, 0L, SEEK_END);
|
||||
// (void) lseek (fh, addr, SEEK_SET);
|
||||
//
|
||||
//}
|
||||
|
||||
/***************************************************************************\
|
||||
* The emulator calls this routine when a SWI instruction is encuntered. The *
|
||||
* parameter passed is the SWI number (lower 24 bits of the instruction). *
|
||||
\***************************************************************************/
|
||||
/* ahe-ykl information is retrieved from elf header and the starting value of
|
||||
brk_static is in sky_info_t */
|
||||
|
||||
/* brk static hold the value of brk */
|
||||
static uint32_t brk_static = -1;
|
||||
|
||||
unsigned
|
||||
ARMul_OSHandleSWI (ARMul_State * state, ARMword number)
|
||||
{
|
||||
number &= 0xfffff;
|
||||
ARMword addr, temp;
|
||||
|
||||
switch (number) {
|
||||
// case SWI_Syscall:
|
||||
// if (state->Reg[7] != 0)
|
||||
// return ARMul_OSHandleSWI(state, state->Reg[7]);
|
||||
// else
|
||||
// return FALSE;
|
||||
// case SWI_Read:
|
||||
// SWIread (state, state->Reg[0], state->Reg[1], state->Reg[2]);
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_GetUID32:
|
||||
// state->Reg[0] = getuid();
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_GetGID32:
|
||||
// state->Reg[0] = getgid();
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_GetEUID32:
|
||||
// state->Reg[0] = geteuid();
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_GetEGID32:
|
||||
// state->Reg[0] = getegid();
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Write:
|
||||
// SWIwrite (state, state->Reg[0], state->Reg[1], state->Reg[2]);
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Open:
|
||||
// SWIopen (state, state->Reg[0], state->Reg[1]);
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Close:
|
||||
// state->Reg[0] = close (state->Reg[0]);
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Seek:{
|
||||
// /* We must return non-zero for failure */
|
||||
// state->Reg[0] =
|
||||
// lseek (state->Reg[0], state->Reg[1],
|
||||
// SEEK_SET);
|
||||
// return TRUE;
|
||||
// }
|
||||
//
|
||||
// case SWI_ExitGroup:
|
||||
// case SWI_Exit:
|
||||
// {
|
||||
// struct timeval tv;
|
||||
// //gettimeofday(&tv,NULL);
|
||||
// //printf("In %s, %d sec, %d usec\n", __FUNCTION__, tv.tv_sec, tv.tv_usec);
|
||||
// printf("passed %d sec, %lld usec\n", get_clock_sec(), get_clock_us());
|
||||
//
|
||||
// /* quit here */
|
||||
// run_command("quit");
|
||||
// return TRUE;
|
||||
// }
|
||||
// case SWI_Times:{
|
||||
// uint32_t dest = state->Reg[0];
|
||||
// struct tms now;
|
||||
// struct target_tms32 nowret;
|
||||
//
|
||||
// uint32_t ret = times(&now);
|
||||
//
|
||||
// if (ret == -1){
|
||||
// debug("syscall %s error %d\n", "SWI_Times", ret);
|
||||
// state->Reg[0] = ret;
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// nowret.tms_cstime = now.tms_cstime;
|
||||
// nowret.tms_cutime = now.tms_cutime;
|
||||
// nowret.tms_stime = now.tms_stime;
|
||||
// nowret.tms_utime = now.tms_utime;
|
||||
//
|
||||
// uint32_t offset;
|
||||
// for (offset = 0; offset < sizeof(nowret); offset++) {
|
||||
// bus_write(8, dest + offset, *((uint8_t *) &nowret + offset));
|
||||
// }
|
||||
//
|
||||
// state->Reg[0] = ret;
|
||||
// return TRUE;
|
||||
// }
|
||||
//
|
||||
// case SWI_Gettimeofday: {
|
||||
// uint32_t dest1 = state->Reg[0];
|
||||
// uint32_t dest2 = state->Reg[1]; // Unsure of this
|
||||
// struct timeval val;
|
||||
// struct timezone zone;
|
||||
// struct target_timeval32 valret;
|
||||
// struct target_timezone32 zoneret;
|
||||
//
|
||||
// uint32_t ret = gettimeofday(&val, &zone);
|
||||
// valret.tv_sec = val.tv_sec;
|
||||
// valret.tv_usec = val.tv_usec;
|
||||
// zoneret.tz_dsttime = zoneret.tz_dsttime;
|
||||
// zoneret.tz_minuteswest = zoneret.tz_minuteswest;
|
||||
//
|
||||
// if (ret == -1){
|
||||
// debug("syscall %s error %d\n", "SWI_Gettimeofday", ret);
|
||||
// state->Reg[0] = ret;
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// uint32_t offset;
|
||||
// if (dest1) {
|
||||
// for (offset = 0; offset < sizeof(valret); offset++) {
|
||||
// bus_write(8, dest1 + offset, *((uint8_t *) &valret + offset));
|
||||
// }
|
||||
// state->Reg[0] = ret;
|
||||
// }
|
||||
// if (dest2) {
|
||||
// for (offset = 0; offset < sizeof(zoneret); offset++) {
|
||||
// bus_write(8, dest2 + offset, *((uint8_t *) &zoneret + offset));
|
||||
// }
|
||||
// state->Reg[0] = ret;
|
||||
// }
|
||||
//
|
||||
// return TRUE;
|
||||
// }
|
||||
// case SWI_Brk:
|
||||
// /* initialize brk value */
|
||||
// /* suppose that brk_static doesn't reach 0xffffffff... */
|
||||
// if (brk_static == -1) {
|
||||
// brk_static = (get_skyeye_pref()->info).brk;
|
||||
// }
|
||||
//
|
||||
// /* FIXME there might be a need to do a mmap */
|
||||
//
|
||||
// if(state->Reg[0]){
|
||||
// if (get_skyeye_exec_info()->mmap_access) {
|
||||
// /* if new brk is greater than current brk, allocate memory */
|
||||
// if (state->Reg[0] > brk_static) {
|
||||
// uint32_t ret = mmap( (void *) brk_static, state->Reg[0] - brk_static,
|
||||
// PROT_WRITE, MAP_PRIVATE | MAP_FIXED | MAP_ANONYMOUS, -1, 0 );
|
||||
// if (ret != MAP_FAILED)
|
||||
// brk_static = ret;
|
||||
// }
|
||||
// }
|
||||
// brk_static = state->Reg[0];
|
||||
// //state->Reg[0] = 0; /* FIXME return value of brk set to be the address on success */
|
||||
// } else {
|
||||
// state->Reg[0] = brk_static;
|
||||
// }
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Break:
|
||||
// state->Emulate = FALSE;
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Mmap:{
|
||||
// int addr = state->Reg[0];
|
||||
// int len = state->Reg[1];
|
||||
// int prot = state->Reg[2];
|
||||
// int flag = state->Reg[3];
|
||||
// int fd = state->Reg[4];
|
||||
// int offset = state->Reg[5];
|
||||
// mmap_area_t *area = new_mmap_area(addr, len);
|
||||
// state->Reg[0] = area->bank.addr;
|
||||
// //printf("syscall %d mmap(0x%x,%x,0x%x,0x%x,%d,0x%x) = 0x%x\n",\
|
||||
// SWI_Mmap, addr, len, prot, flag, fd, offset, state->Reg[0]);
|
||||
// return TRUE;
|
||||
// }
|
||||
//
|
||||
// case SWI_Munmap:
|
||||
// state->Reg[0] = 0;
|
||||
// return TRUE;
|
||||
//
|
||||
// case SWI_Mmap2:{
|
||||
// int addr = state->Reg[0];
|
||||
// int len = state->Reg[1];
|
||||
// int prot = state->Reg[2];
|
||||
// int flag = state->Reg[3];
|
||||
// int fd = state->Reg[4];
|
||||
// int offset = state->Reg[5] * 4096; /* page offset */
|
||||
// mmap_area_t *area = new_mmap_area(addr, len);
|
||||
// state->Reg[0] = area->bank.addr;
|
||||
//
|
||||
// return TRUE;
|
||||
// }
|
||||
//
|
||||
// case SWI_Breakpoint:
|
||||
// //chy 2005-09-12 change below line
|
||||
// //state->EndCondition = RDIError_BreakpointReached;
|
||||
// //printf ("SKYEYE: in armos.c : should not come here!!!!\n");
|
||||
// state->EndCondition = 0;
|
||||
// /*modified by ksh to support breakpoiont*/
|
||||
// state->Emulate = STOP;
|
||||
// return (TRUE);
|
||||
// case SWI_Uname:
|
||||
// {
|
||||
// struct utsname *uts = (uintptr_t) state->Reg[0]; /* uname should write data in this address */
|
||||
// struct utsname utsbuf;
|
||||
// //printf("Uname size is %x\n", sizeof(utsbuf));
|
||||
// char *buf;
|
||||
// uintptr_t sp ; /* used as a temporary address */
|
||||
//
|
||||
//#define COPY_UTS_STRING(addr) \
|
||||
// buf = addr; \
|
||||
// while(*buf != NULL) { \
|
||||
// bus_write(8, sp, *buf); \
|
||||
// sp++; \
|
||||
// buf++; \
|
||||
// }
|
||||
//#define COPY_UTS(field) /*printf("%s: %s at %p\n", #field, utsbuf.field, uts->field);*/ \
|
||||
// sp = (uintptr_t) uts->field; \
|
||||
// COPY_UTS_STRING((&utsbuf)->field);
|
||||
//
|
||||
// if (uname(&utsbuf) < 0) {
|
||||
// printf("syscall uname: utsname error\n");
|
||||
// state->Reg[0] = -1;
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// /* FIXME for now, this is just the host system call
|
||||
// Some data should be missing, as it depends on
|
||||
// the version of utsname */
|
||||
// COPY_UTS(sysname);
|
||||
// COPY_UTS(nodename);
|
||||
// COPY_UTS(release);
|
||||
// COPY_UTS(version);
|
||||
// COPY_UTS(machine);
|
||||
//
|
||||
// state->Reg[0] = 0;
|
||||
// return TRUE;
|
||||
// }
|
||||
// case SWI_Fcntl:
|
||||
// {
|
||||
// uint32_t fd = state->Reg[0];
|
||||
// uint32_t cmd = state->Reg[1];
|
||||
// uint32_t arg = state->Reg[2];
|
||||
// uint32_t ret;
|
||||
//
|
||||
// switch(cmd){
|
||||
// case (F_GETFD):
|
||||
// {
|
||||
// ret = fcntl(fd, cmd, arg);
|
||||
// //printf("syscall fcntl for getfd not implemented, ret %d\n", ret);
|
||||
// state->Reg[0] = ret;
|
||||
// return FALSE;
|
||||
// }
|
||||
// default:
|
||||
// break;
|
||||
// }
|
||||
//
|
||||
// printf("syscall fcntl unimplemented fd %x cmd %x\n", fd, cmd);
|
||||
// state->Reg[0] = -1;
|
||||
// return FALSE;
|
||||
//
|
||||
// }
|
||||
// case SWI_Fstat64:
|
||||
// {
|
||||
// uint32_t dest = state->Reg[1];
|
||||
// uint32_t fd = state->Reg[0];
|
||||
// struct stat64 statbuf;
|
||||
// struct target_stat64 statret;
|
||||
// memset(&statret, 0, sizeof(struct target_stat64));
|
||||
// uint32_t ret = fstat64(fd, &statbuf);
|
||||
//
|
||||
// if (ret == -1){
|
||||
// printf("syscall %s returned error\n", "SWI_Fstat");
|
||||
// state->Reg[0] = ret;
|
||||
// return FALSE;
|
||||
// }
|
||||
//
|
||||
// /* copy statbuf to the process memory space
|
||||
// FIXME can't say if endian has an effect here */
|
||||
// uint32_t offset;
|
||||
// //printf("Fstat system is size %x\n", sizeof(statbuf));
|
||||
// //printf("Fstat target is size %x\n", sizeof(statret));
|
||||
//
|
||||
// /* we copy system structure data stat64 into arm fixed size structure target_stat64 */
|
||||
// statret.st_dev = statbuf.st_dev;
|
||||
// statret.st_ino = statbuf.st_ino;
|
||||
// statret.st_mode = statbuf.st_mode;
|
||||
// statret.st_nlink = statbuf.st_nlink;
|
||||
// statret.st_uid = statbuf.st_uid;
|
||||
// statret.st_gid = statbuf.st_gid;
|
||||
// statret.st_rdev = statbuf.st_rdev;
|
||||
// statret.st_size = statbuf.st_size;
|
||||
// statret.st_blksize = statbuf.st_blksize;
|
||||
// statret.st_blocks = statbuf.st_blocks;
|
||||
// statret.st32_atime = statbuf.st_atime;
|
||||
// statret.st32_mtime = statbuf.st_mtime;
|
||||
// statret.st32_ctime = statbuf.st_ctime;
|
||||
//
|
||||
// for (offset = 0; offset < sizeof(statret); offset++) {
|
||||
// bus_write(8, dest + offset, *((uint8_t *) &statret + offset));
|
||||
// }
|
||||
//
|
||||
// state->Reg[0] = ret;
|
||||
// return TRUE;
|
||||
// }
|
||||
// case SWI_Set_tls:
|
||||
// {
|
||||
// //printf("syscall set_tls unimplemented\n");
|
||||
// state->mmu.thread_uro_id = state->Reg[0];
|
||||
// state->CP15[CP15_THREAD_URO - CP15_BASE] = state->Reg[0];
|
||||
// state->Reg[0] = 0;
|
||||
// return FALSE;
|
||||
// }
|
||||
//#if 0
|
||||
// case SWI_Clock:
|
||||
// /* return number of centi-seconds... */
|
||||
// state->Reg[0] =
|
||||
//#ifdef CLOCKS_PER_SEC
|
||||
// (CLOCKS_PER_SEC >= 100)
|
||||
// ? (ARMword) (clock () / (CLOCKS_PER_SEC / 100))
|
||||
// : (ARMword) ((clock () * 100) / CLOCKS_PER_SEC);
|
||||
//#else
|
||||
// /* presume unix... clock() returns microseconds */
|
||||
// (ARMword) (clock () / 10000);
|
||||
//#endif
|
||||
// return (TRUE);
|
||||
//
|
||||
// case SWI_Time:
|
||||
// state->Reg[0] = (ARMword) time (NULL);
|
||||
// return (TRUE);
|
||||
// case SWI_Flen:
|
||||
// SWIflen (state, state->Reg[0]);
|
||||
// return (TRUE);
|
||||
//
|
||||
//#endif
|
||||
default:
|
||||
|
||||
_dbg_assert_msg_(ARM11, false, "ImplementMe: ARMul_OSHandleSWI!");
|
||||
|
||||
return (FALSE);
|
||||
}
|
||||
}
|
||||
//
|
||||
///**
|
||||
// * @brief For mmap syscall.A mmap_area is a memory bank. Get from ppc.
|
||||
// */
|
||||
//static mmap_area_t* new_mmap_area(int sim_addr, int len){
|
||||
// mmap_area_t *area = (mmap_area_t *)malloc(sizeof(mmap_area_t));
|
||||
// if(area == NULL){
|
||||
// printf("error, failed %s\n",__FUNCTION__);
|
||||
// exit(0);
|
||||
// }
|
||||
//#if FAST_MEMORY
|
||||
// if (mmap_next_base == -1)
|
||||
// {
|
||||
// mmap_next_base = get_skyeye_exec_info()->brk;
|
||||
// }
|
||||
//#endif
|
||||
//
|
||||
// memset(area, 0x0, sizeof(mmap_area_t));
|
||||
// area->bank.addr = mmap_next_base;
|
||||
// area->bank.len = len;
|
||||
// area->bank.bank_write = mmap_mem_write;
|
||||
// area->bank.bank_read = mmap_mem_read;
|
||||
// area->bank.type = MEMTYPE_RAM;
|
||||
// area->bank.objname = "mmap";
|
||||
// addr_mapping(&area->bank);
|
||||
//
|
||||
//#if FAST_MEMORY
|
||||
// if (get_skyeye_exec_info()->mmap_access)
|
||||
// {
|
||||
// /* FIXME check proper flags */
|
||||
// /* FIXME we may delete the need of banks up there */
|
||||
// uint32_t ret = mmap(mmap_next_base, len, PROT_WRITE | PROT_READ, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
|
||||
// mmap_next_base = ret;
|
||||
// }
|
||||
// area->mmap_addr = (uint8_t*)get_dma_addr(mmap_next_base);
|
||||
//#else
|
||||
// area->mmap_addr = malloc(len);
|
||||
// if(area->mmap_addr == NULL){
|
||||
// printf("error mmap malloc\n");
|
||||
// exit(0);
|
||||
// }
|
||||
// memset(area->mmap_addr, 0x0, len);
|
||||
//#endif
|
||||
//
|
||||
// area->next = NULL;
|
||||
// if(mmap_global){
|
||||
// area->next = mmap_global->next;
|
||||
// mmap_global->next = area;
|
||||
// }else{
|
||||
// mmap_global = area;
|
||||
// }
|
||||
// mmap_next_base = mmap_next_base + len;
|
||||
// return area;
|
||||
//}
|
||||
//
|
||||
//static mmap_area_t *get_mmap_area(int addr){
|
||||
// mmap_area_t *tmp = mmap_global;
|
||||
// while(tmp){
|
||||
// if ((tmp->bank.addr <= addr) && (tmp->bank.addr + tmp->bank.len > addr)){
|
||||
// return tmp;
|
||||
// }
|
||||
// tmp = tmp->next;
|
||||
// }
|
||||
// printf("cannot get mmap area:addr=0x%x\n", addr);
|
||||
// return NULL;
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * @brief the mmap_area bank write function. Get from ppc.
|
||||
// *
|
||||
// * @param size size to write, 8/16/32
|
||||
// * @param addr address to write
|
||||
// * @param value value to write
|
||||
// *
|
||||
// * @return sucess return 1,otherwise 0.
|
||||
// */
|
||||
//static char mmap_mem_write(short size, int addr, uint32_t value){
|
||||
// mmap_area_t *area_tmp = get_mmap_area(addr);
|
||||
// mem_bank_t *bank_tmp = &area_tmp->bank;
|
||||
// int offset = addr - bank_tmp->addr;
|
||||
// switch(size){
|
||||
// case 8:{
|
||||
// //uint8_t value_endian = value;
|
||||
// uint8_t value_endian = (uint8_t)value;
|
||||
// *(uint8_t *)&(((char *)area_tmp->mmap_addr)[offset]) = value_endian;
|
||||
// debug("in %s,size=%d,addr=0x%x,value=0x%x\n",__FUNCTION__,size,addr,value_endian);
|
||||
// break;
|
||||
// }
|
||||
// case 16:{
|
||||
// //uint16_t value_endian = half_to_BE((uint16_t)value);
|
||||
// uint16_t value_endian = ((uint16_t)value);
|
||||
// *(uint16_t *)&(((char *)area_tmp->mmap_addr)[offset]) = value_endian;
|
||||
// debug("in %s,size=%d,addr=0x%x,value=0x%x\n",__FUNCTION__,size,addr,value_endian);
|
||||
// break;
|
||||
// }
|
||||
// case 32:{
|
||||
// //uint32_t value_endian = word_to_BE((uint32_t)value);
|
||||
// uint32_t value_endian = ((uint32_t)value);
|
||||
// *(uint32_t *)&(((char *)area_tmp->mmap_addr)[offset]) = value_endian;
|
||||
// debug("in %s,size=%d,addr=0x%x,value=0x%x\n",__FUNCTION__,size,addr,value_endian);
|
||||
// break;
|
||||
// }
|
||||
// default:
|
||||
// printf("invalid size %d\n",size);
|
||||
// return 0;
|
||||
// }
|
||||
// return 1;
|
||||
//}
|
||||
//
|
||||
///**
|
||||
// * @brief the mmap_area bank read function. Get from ppc.
|
||||
// *
|
||||
// * @param size size to read, 8/16/32
|
||||
// * @param addr address to read
|
||||
// * @param value value to read
|
||||
// *
|
||||
// * @return sucess return 1,otherwise 0.
|
||||
// */
|
||||
//static char mmap_mem_read(short size, int addr, uint32_t * value){
|
||||
// mmap_area_t *area_tmp = get_mmap_area(addr);
|
||||
// mem_bank_t *bank_tmp = &area_tmp->bank;
|
||||
// int offset = addr - bank_tmp->addr;
|
||||
// switch(size){
|
||||
// case 8:{
|
||||
// //*(uint8_t *)value = *(uint8_t *)&(((uint8_t *)area_tmp->mmap_addr)[offset]);
|
||||
// *value = *(uint8_t *)&(((uint8_t *)area_tmp->mmap_addr)[offset]);
|
||||
// debug("in %s,size=%d,addr=0x%x,value=0x%x\n",__FUNCTION__,size,addr,*(uint32_t*)value);
|
||||
// break;
|
||||
// }
|
||||
// case 16:{
|
||||
// //*(uint16_t *)value = half_from_BE(*(uint16_t *)&(((uint8_t *)area_tmp->mmap_addr)[offset]));
|
||||
// *value = (*(uint16_t *)&(((uint8_t *)area_tmp->mmap_addr)[offset]));
|
||||
// debug("in %s,size=%d,addr=0x%x,value=0x%x\n",__FUNCTION__,size,addr,*(uint16_t*)value);
|
||||
// break;
|
||||
// }
|
||||
// case 32:
|
||||
// //*value = (uint32_t)word_from_BE(*(uint32_t *)&(((uint8_t *)area_tmp->mmap_addr)[offset]));
|
||||
// *value = (uint32_t)(*(uint32_t *)&(((uint8_t *)area_tmp->mmap_addr)[offset]));
|
||||
// debug("in %s,size=%d,addr=0x%x,value=0x%x\n",__FUNCTION__,size,addr,*(uint32_t*)value);
|
||||
// break;
|
||||
// default:
|
||||
// printf("invalid size %d\n",size);
|
||||
// return 0;
|
||||
// }
|
||||
// return 1;
|
||||
//}
|
953
src/core/src/arm/armsupp.cpp
Normal file
953
src/core/src/arm/armsupp.cpp
Normal file
@ -0,0 +1,953 @@
|
||||
/* armsupp.c -- ARMulator support code: ARM6 Instruction Emulator.
|
||||
Copyright (C) 1994 Advanced RISC Machines Ltd.
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
#include "armdefs.h"
|
||||
#include "armemu.h"
|
||||
//#include "ansidecl.h"
|
||||
#include "skyeye_defs.h"
|
||||
unsigned xscale_cp15_cp_access_allowed (ARMul_State * state, unsigned reg,
|
||||
unsigned cpnum);
|
||||
//extern int skyeye_instr_debug;
|
||||
/* Definitions for the support routines. */
|
||||
|
||||
static ARMword ModeToBank (ARMword);
|
||||
static void EnvokeList (ARMul_State *, unsigned int, unsigned int);
|
||||
|
||||
struct EventNode
|
||||
{ /* An event list node. */
|
||||
unsigned (*func) (ARMul_State *); /* The function to call. */
|
||||
struct EventNode *next;
|
||||
};
|
||||
|
||||
/* This routine returns the value of a register from a mode. */
|
||||
|
||||
ARMword
|
||||
ARMul_GetReg (ARMul_State * state, unsigned mode, unsigned reg)
|
||||
{
|
||||
mode &= MODEBITS;
|
||||
if (mode != state->Mode)
|
||||
return (state->RegBank[ModeToBank ((ARMword) mode)][reg]);
|
||||
else
|
||||
return (state->Reg[reg]);
|
||||
}
|
||||
|
||||
/* This routine sets the value of a register for a mode. */
|
||||
|
||||
void
|
||||
ARMul_SetReg (ARMul_State * state, unsigned mode, unsigned reg, ARMword value)
|
||||
{
|
||||
mode &= MODEBITS;
|
||||
if (mode != state->Mode)
|
||||
state->RegBank[ModeToBank ((ARMword) mode)][reg] = value;
|
||||
else
|
||||
state->Reg[reg] = value;
|
||||
}
|
||||
|
||||
/* This routine returns the value of the PC, mode independently. */
|
||||
|
||||
ARMword
|
||||
ARMul_GetPC (ARMul_State * state)
|
||||
{
|
||||
if (state->Mode > SVC26MODE)
|
||||
return state->Reg[15];
|
||||
else
|
||||
return R15PC;
|
||||
}
|
||||
|
||||
/* This routine returns the value of the PC, mode independently. */
|
||||
|
||||
ARMword
|
||||
ARMul_GetNextPC (ARMul_State * state)
|
||||
{
|
||||
if (state->Mode > SVC26MODE)
|
||||
return state->Reg[15] + isize;
|
||||
else
|
||||
return (state->Reg[15] + isize) & R15PCBITS;
|
||||
}
|
||||
|
||||
/* This routine sets the value of the PC. */
|
||||
|
||||
void
|
||||
ARMul_SetPC (ARMul_State * state, ARMword value)
|
||||
{
|
||||
if (ARMul_MODE32BIT)
|
||||
state->Reg[15] = value & PCBITS;
|
||||
else
|
||||
state->Reg[15] = R15CCINTMODE | (value & R15PCBITS);
|
||||
FLUSHPIPE;
|
||||
}
|
||||
|
||||
/* This routine returns the value of register 15, mode independently. */
|
||||
|
||||
ARMword
|
||||
ARMul_GetR15 (ARMul_State * state)
|
||||
{
|
||||
if (state->Mode > SVC26MODE)
|
||||
return (state->Reg[15]);
|
||||
else
|
||||
return (R15PC | ECC | ER15INT | EMODE);
|
||||
}
|
||||
|
||||
/* This routine sets the value of Register 15. */
|
||||
|
||||
void
|
||||
ARMul_SetR15 (ARMul_State * state, ARMword value)
|
||||
{
|
||||
if (ARMul_MODE32BIT)
|
||||
state->Reg[15] = value & PCBITS;
|
||||
else {
|
||||
state->Reg[15] = value;
|
||||
ARMul_R15Altered (state);
|
||||
}
|
||||
FLUSHPIPE;
|
||||
}
|
||||
|
||||
/* This routine returns the value of the CPSR. */
|
||||
|
||||
ARMword
|
||||
ARMul_GetCPSR (ARMul_State * state)
|
||||
{
|
||||
//chy 2003-08-20: below is from gdb20030716, maybe isn't suitable for system simulator
|
||||
//return (CPSR | state->Cpsr); for gdb20030716
|
||||
return (CPSR); //had be tested in old skyeye with gdb5.0-5.3
|
||||
}
|
||||
|
||||
/* This routine sets the value of the CPSR. */
|
||||
|
||||
void
|
||||
ARMul_SetCPSR (ARMul_State * state, ARMword value)
|
||||
{
|
||||
state->Cpsr = value;
|
||||
ARMul_CPSRAltered (state);
|
||||
}
|
||||
|
||||
/* This routine does all the nasty bits involved in a write to the CPSR,
|
||||
including updating the register bank, given a MSR instruction. */
|
||||
|
||||
void
|
||||
ARMul_FixCPSR (ARMul_State * state, ARMword instr, ARMword rhs)
|
||||
{
|
||||
state->Cpsr = ARMul_GetCPSR (state);
|
||||
//chy 2006-02-16 , should not consider system mode, don't conside 26bit mode
|
||||
if (state->Mode != USER26MODE && state->Mode != USER32MODE ) {
|
||||
/* In user mode, only write flags. */
|
||||
if (BIT (16))
|
||||
SETPSR_C (state->Cpsr, rhs);
|
||||
if (BIT (17))
|
||||
SETPSR_X (state->Cpsr, rhs);
|
||||
if (BIT (18))
|
||||
SETPSR_S (state->Cpsr, rhs);
|
||||
}
|
||||
if (BIT (19))
|
||||
SETPSR_F (state->Cpsr, rhs);
|
||||
ARMul_CPSRAltered (state);
|
||||
}
|
||||
|
||||
/* Get an SPSR from the specified mode. */
|
||||
|
||||
ARMword
|
||||
ARMul_GetSPSR (ARMul_State * state, ARMword mode)
|
||||
{
|
||||
ARMword bank = ModeToBank (mode & MODEBITS);
|
||||
|
||||
if (!BANK_CAN_ACCESS_SPSR (bank))
|
||||
return ARMul_GetCPSR (state);
|
||||
|
||||
return state->Spsr[bank];
|
||||
}
|
||||
|
||||
/* This routine does a write to an SPSR. */
|
||||
|
||||
void
|
||||
ARMul_SetSPSR (ARMul_State * state, ARMword mode, ARMword value)
|
||||
{
|
||||
ARMword bank = ModeToBank (mode & MODEBITS);
|
||||
|
||||
if (BANK_CAN_ACCESS_SPSR (bank))
|
||||
state->Spsr[bank] = value;
|
||||
}
|
||||
|
||||
/* This routine does a write to the current SPSR, given an MSR instruction. */
|
||||
|
||||
void
|
||||
ARMul_FixSPSR (ARMul_State * state, ARMword instr, ARMword rhs)
|
||||
{
|
||||
if (BANK_CAN_ACCESS_SPSR (state->Bank)) {
|
||||
if (BIT (16))
|
||||
SETPSR_C (state->Spsr[state->Bank], rhs);
|
||||
if (BIT (17))
|
||||
SETPSR_X (state->Spsr[state->Bank], rhs);
|
||||
if (BIT (18))
|
||||
SETPSR_S (state->Spsr[state->Bank], rhs);
|
||||
if (BIT (19))
|
||||
SETPSR_F (state->Spsr[state->Bank], rhs);
|
||||
}
|
||||
}
|
||||
|
||||
/* This routine updates the state of the emulator after the Cpsr has been
|
||||
changed. Both the processor flags and register bank are updated. */
|
||||
|
||||
void
|
||||
ARMul_CPSRAltered (ARMul_State * state)
|
||||
{
|
||||
ARMword oldmode;
|
||||
|
||||
if (state->prog32Sig == LOW)
|
||||
state->Cpsr &= (CCBITS | INTBITS | R15MODEBITS);
|
||||
|
||||
oldmode = state->Mode;
|
||||
|
||||
if (state->Mode != (state->Cpsr & MODEBITS)) {
|
||||
state->Mode =
|
||||
ARMul_SwitchMode (state, state->Mode,
|
||||
state->Cpsr & MODEBITS);
|
||||
|
||||
state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
|
||||
}
|
||||
//state->Cpsr &= ~MODEBITS;
|
||||
|
||||
ASSIGNINT (state->Cpsr & INTBITS);
|
||||
//state->Cpsr &= ~INTBITS;
|
||||
ASSIGNN ((state->Cpsr & NBIT) != 0);
|
||||
//state->Cpsr &= ~NBIT;
|
||||
ASSIGNZ ((state->Cpsr & ZBIT) != 0);
|
||||
//state->Cpsr &= ~ZBIT;
|
||||
ASSIGNC ((state->Cpsr & CBIT) != 0);
|
||||
//state->Cpsr &= ~CBIT;
|
||||
ASSIGNV ((state->Cpsr & VBIT) != 0);
|
||||
//state->Cpsr &= ~VBIT;
|
||||
ASSIGNS ((state->Cpsr & SBIT) != 0);
|
||||
//state->Cpsr &= ~SBIT;
|
||||
#ifdef MODET
|
||||
ASSIGNT ((state->Cpsr & TBIT) != 0);
|
||||
//state->Cpsr &= ~TBIT;
|
||||
#endif
|
||||
|
||||
if (oldmode > SVC26MODE) {
|
||||
if (state->Mode <= SVC26MODE) {
|
||||
state->Emulate = CHANGEMODE;
|
||||
state->Reg[15] = ECC | ER15INT | EMODE | R15PC;
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (state->Mode > SVC26MODE) {
|
||||
state->Emulate = CHANGEMODE;
|
||||
state->Reg[15] = R15PC;
|
||||
}
|
||||
else
|
||||
state->Reg[15] = ECC | ER15INT | EMODE | R15PC;
|
||||
}
|
||||
}
|
||||
|
||||
/* This routine updates the state of the emulator after register 15 has
|
||||
been changed. Both the processor flags and register bank are updated.
|
||||
This routine should only be called from a 26 bit mode. */
|
||||
|
||||
void
|
||||
ARMul_R15Altered (ARMul_State * state)
|
||||
{
|
||||
if (state->Mode != R15MODE) {
|
||||
state->Mode = ARMul_SwitchMode (state, state->Mode, R15MODE);
|
||||
state->NtransSig = (state->Mode & 3) ? HIGH : LOW;
|
||||
}
|
||||
|
||||
if (state->Mode > SVC26MODE)
|
||||
state->Emulate = CHANGEMODE;
|
||||
|
||||
ASSIGNR15INT (R15INT);
|
||||
|
||||
ASSIGNN ((state->Reg[15] & NBIT) != 0);
|
||||
ASSIGNZ ((state->Reg[15] & ZBIT) != 0);
|
||||
ASSIGNC ((state->Reg[15] & CBIT) != 0);
|
||||
ASSIGNV ((state->Reg[15] & VBIT) != 0);
|
||||
}
|
||||
|
||||
/* This routine controls the saving and restoring of registers across mode
|
||||
changes. The regbank matrix is largely unused, only rows 13 and 14 are
|
||||
used across all modes, 8 to 14 are used for FIQ, all others use the USER
|
||||
column. It's easier this way. old and new parameter are modes numbers.
|
||||
Notice the side effect of changing the Bank variable. */
|
||||
|
||||
ARMword
|
||||
ARMul_SwitchMode (ARMul_State * state, ARMword oldmode, ARMword newmode)
|
||||
{
|
||||
unsigned i;
|
||||
ARMword oldbank;
|
||||
ARMword newbank;
|
||||
static int revision_value = 53;
|
||||
|
||||
oldbank = ModeToBank (oldmode);
|
||||
newbank = state->Bank = ModeToBank (newmode);
|
||||
|
||||
/* Do we really need to do it? */
|
||||
if (oldbank != newbank) {
|
||||
if (oldbank == 3 && newbank == 2) {
|
||||
//printf("icounter is %d PC is %x MODE CHANGED : %d --> %d\n", state->NumInstrs, state->pc, oldbank, newbank);
|
||||
if (state->NumInstrs >= 5832487) {
|
||||
// printf("%d, ", state->NumInstrs + revision_value);
|
||||
// printf("revision_value : %d\n", revision_value);
|
||||
revision_value ++;
|
||||
}
|
||||
}
|
||||
/* Save away the old registers. */
|
||||
switch (oldbank) {
|
||||
case USERBANK:
|
||||
case IRQBANK:
|
||||
case SVCBANK:
|
||||
case ABORTBANK:
|
||||
case UNDEFBANK:
|
||||
if (newbank == FIQBANK)
|
||||
for (i = 8; i < 13; i++)
|
||||
state->RegBank[USERBANK][i] =
|
||||
state->Reg[i];
|
||||
state->RegBank[oldbank][13] = state->Reg[13];
|
||||
state->RegBank[oldbank][14] = state->Reg[14];
|
||||
break;
|
||||
case FIQBANK:
|
||||
for (i = 8; i < 15; i++)
|
||||
state->RegBank[FIQBANK][i] = state->Reg[i];
|
||||
break;
|
||||
case DUMMYBANK:
|
||||
for (i = 8; i < 15; i++)
|
||||
state->RegBank[DUMMYBANK][i] = 0;
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
|
||||
/* Restore the new registers. */
|
||||
switch (newbank) {
|
||||
case USERBANK:
|
||||
case IRQBANK:
|
||||
case SVCBANK:
|
||||
case ABORTBANK:
|
||||
case UNDEFBANK:
|
||||
if (oldbank == FIQBANK)
|
||||
for (i = 8; i < 13; i++)
|
||||
state->Reg[i] =
|
||||
state->RegBank[USERBANK][i];
|
||||
state->Reg[13] = state->RegBank[newbank][13];
|
||||
state->Reg[14] = state->RegBank[newbank][14];
|
||||
break;
|
||||
case FIQBANK:
|
||||
for (i = 8; i < 15; i++)
|
||||
state->Reg[i] = state->RegBank[FIQBANK][i];
|
||||
break;
|
||||
case DUMMYBANK:
|
||||
for (i = 8; i < 15; i++)
|
||||
state->Reg[i] = 0;
|
||||
break;
|
||||
default:
|
||||
abort ();
|
||||
}
|
||||
}
|
||||
|
||||
return newmode;
|
||||
}
|
||||
|
||||
/* Given a processor mode, this routine returns the
|
||||
register bank that will be accessed in that mode. */
|
||||
|
||||
static ARMword
|
||||
ModeToBank (ARMword mode)
|
||||
{
|
||||
static ARMword bankofmode[] = {
|
||||
USERBANK, FIQBANK, IRQBANK, SVCBANK,
|
||||
DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK,
|
||||
DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK,
|
||||
DUMMYBANK, DUMMYBANK, DUMMYBANK, DUMMYBANK,
|
||||
USERBANK, FIQBANK, IRQBANK, SVCBANK,
|
||||
DUMMYBANK, DUMMYBANK, DUMMYBANK, ABORTBANK,
|
||||
DUMMYBANK, DUMMYBANK, DUMMYBANK, UNDEFBANK,
|
||||
DUMMYBANK, DUMMYBANK, DUMMYBANK, SYSTEMBANK
|
||||
};
|
||||
|
||||
if (mode >= (sizeof (bankofmode) / sizeof (bankofmode[0])))
|
||||
return DUMMYBANK;
|
||||
|
||||
return bankofmode[mode];
|
||||
}
|
||||
|
||||
/* Returns the register number of the nth register in a reg list. */
|
||||
|
||||
unsigned
|
||||
ARMul_NthReg (ARMword instr, unsigned number)
|
||||
{
|
||||
unsigned bit, upto;
|
||||
|
||||
for (bit = 0, upto = 0; upto <= number; bit++)
|
||||
if (BIT (bit))
|
||||
upto++;
|
||||
|
||||
return (bit - 1);
|
||||
}
|
||||
|
||||
/* Assigns the N and Z flags depending on the value of result. */
|
||||
|
||||
void
|
||||
ARMul_NegZero (ARMul_State * state, ARMword result)
|
||||
{
|
||||
if (NEG (result)) {
|
||||
SETN;
|
||||
CLEARZ;
|
||||
}
|
||||
else if (result == 0) {
|
||||
CLEARN;
|
||||
SETZ;
|
||||
}
|
||||
else {
|
||||
CLEARN;
|
||||
CLEARZ;
|
||||
}
|
||||
}
|
||||
|
||||
/* Compute whether an addition of A and B, giving RESULT, overflowed. */
|
||||
|
||||
int
|
||||
AddOverflow (ARMword a, ARMword b, ARMword result)
|
||||
{
|
||||
return ((NEG (a) && NEG (b) && POS (result))
|
||||
|| (POS (a) && POS (b) && NEG (result)));
|
||||
}
|
||||
|
||||
/* Compute whether a subtraction of A and B, giving RESULT, overflowed. */
|
||||
|
||||
int
|
||||
SubOverflow (ARMword a, ARMword b, ARMword result)
|
||||
{
|
||||
return ((NEG (a) && POS (b) && POS (result))
|
||||
|| (POS (a) && NEG (b) && NEG (result)));
|
||||
}
|
||||
|
||||
/* Assigns the C flag after an addition of a and b to give result. */
|
||||
|
||||
void
|
||||
ARMul_AddCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result)
|
||||
{
|
||||
ASSIGNC ((NEG (a) && NEG (b)) ||
|
||||
(NEG (a) && POS (result)) || (NEG (b) && POS (result)));
|
||||
}
|
||||
|
||||
/* Assigns the V flag after an addition of a and b to give result. */
|
||||
|
||||
void
|
||||
ARMul_AddOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
|
||||
{
|
||||
ASSIGNV (AddOverflow (a, b, result));
|
||||
}
|
||||
|
||||
/* Assigns the C flag after an subtraction of a and b to give result. */
|
||||
|
||||
void
|
||||
ARMul_SubCarry (ARMul_State * state, ARMword a, ARMword b, ARMword result)
|
||||
{
|
||||
ASSIGNC ((NEG (a) && POS (b)) ||
|
||||
(NEG (a) && POS (result)) || (POS (b) && POS (result)));
|
||||
}
|
||||
|
||||
/* Assigns the V flag after an subtraction of a and b to give result. */
|
||||
|
||||
void
|
||||
ARMul_SubOverflow (ARMul_State * state, ARMword a, ARMword b, ARMword result)
|
||||
{
|
||||
ASSIGNV (SubOverflow (a, b, result));
|
||||
}
|
||||
|
||||
/* This function does the work of generating the addresses used in an
|
||||
LDC instruction. The code here is always post-indexed, it's up to the
|
||||
caller to get the input address correct and to handle base register
|
||||
modification. It also handles the Busy-Waiting. */
|
||||
|
||||
void
|
||||
ARMul_LDC (ARMul_State * state, ARMword instr, ARMword address)
|
||||
{
|
||||
unsigned cpab;
|
||||
ARMword data;
|
||||
|
||||
UNDEF_LSCPCBaseWb;
|
||||
//printf("SKYEYE ARMul_LDC, CPnum is %x, instr %x, addr %x\n",CPNum, instr, address);
|
||||
/*chy 2004-05-23 should update this function in the future,should concern dataabort*/
|
||||
// chy 2004-05-25 , fix it now,so needn't printf
|
||||
// printf("SKYEYE ARMul_LDC, should update this function!!!!!\n");
|
||||
//exit(-1);
|
||||
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
/*
|
||||
printf
|
||||
("SKYEYE ARMul_LDC,NOT ALLOW, underinstr, CPnum is %x, instr %x, addr %x\n",
|
||||
CPNum, instr, address);
|
||||
*/
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ADDREXCEPT (address))
|
||||
INTERNALABORT (address);
|
||||
|
||||
cpab = (state->LDC[CPNum]) (state, ARMul_FIRST, instr, 0);
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->LDC[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
cpab = (state->LDC[CPNum]) (state, ARMul_BUSY, instr,
|
||||
0);
|
||||
}
|
||||
if (cpab == ARMul_CANT) {
|
||||
/*
|
||||
printf
|
||||
("SKYEYE ARMul_LDC,NOT CAN, underinstr, CPnum is %x, instr %x, addr %x\n",
|
||||
CPNum, instr, address);
|
||||
*/
|
||||
CPTAKEABORT;
|
||||
return;
|
||||
}
|
||||
|
||||
cpab = (state->LDC[CPNum]) (state, ARMul_TRANSFER, instr, 0);
|
||||
data = ARMul_LoadWordN (state, address);
|
||||
//chy 2004-05-25
|
||||
if (state->abortSig || state->Aborted)
|
||||
goto L_ldc_takeabort;
|
||||
|
||||
BUSUSEDINCPCN;
|
||||
//chy 2004-05-25
|
||||
/*
|
||||
if (BIT (21))
|
||||
LSBase = state->Base;
|
||||
*/
|
||||
|
||||
cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data);
|
||||
|
||||
while (cpab == ARMul_INC) {
|
||||
address += 4;
|
||||
data = ARMul_LoadWordN (state, address);
|
||||
//chy 2004-05-25
|
||||
if (state->abortSig || state->Aborted)
|
||||
goto L_ldc_takeabort;
|
||||
|
||||
cpab = (state->LDC[CPNum]) (state, ARMul_DATA, instr, data);
|
||||
}
|
||||
|
||||
//chy 2004-05-25
|
||||
L_ldc_takeabort:
|
||||
if (BIT (21)) {
|
||||
if (!
|
||||
((state->abortSig || state->Aborted)
|
||||
&& state->lateabtSig == LOW))
|
||||
LSBase = state->Base;
|
||||
}
|
||||
|
||||
if (state->abortSig || state->Aborted)
|
||||
TAKEABORT;
|
||||
}
|
||||
|
||||
/* This function does the work of generating the addresses used in an
|
||||
STC instruction. The code here is always post-indexed, it's up to the
|
||||
caller to get the input address correct and to handle base register
|
||||
modification. It also handles the Busy-Waiting. */
|
||||
|
||||
void
|
||||
ARMul_STC (ARMul_State * state, ARMword instr, ARMword address)
|
||||
{
|
||||
unsigned cpab;
|
||||
ARMword data;
|
||||
|
||||
UNDEF_LSCPCBaseWb;
|
||||
|
||||
//printf("SKYEYE ARMul_STC, CPnum is %x, instr %x, addr %x\n",CPNum, instr, address);
|
||||
/*chy 2004-05-23 should update this function in the future,should concern dataabort */
|
||||
// skyeye_instr_debug=0;printf("SKYEYE debug end!!!!\n");
|
||||
// chy 2004-05-25 , fix it now,so needn't printf
|
||||
// printf("SKYEYE ARMul_STC, should update this function!!!!!\n");
|
||||
|
||||
//exit(-1);
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
/*
|
||||
printf
|
||||
("SKYEYE ARMul_STC,NOT ALLOW, undefinstr, CPnum is %x, instr %x, addr %x\n",
|
||||
CPNum, instr, address);
|
||||
*/
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return;
|
||||
}
|
||||
|
||||
if (ADDREXCEPT (address) || VECTORACCESS (address))
|
||||
INTERNALABORT (address);
|
||||
|
||||
cpab = (state->STC[CPNum]) (state, ARMul_FIRST, instr, &data);
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->STC[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
cpab = (state->STC[CPNum]) (state, ARMul_BUSY, instr,
|
||||
&data);
|
||||
}
|
||||
|
||||
if (cpab == ARMul_CANT) {
|
||||
/*
|
||||
printf
|
||||
("SKYEYE ARMul_STC,CANT, undefinstr, CPnum is %x, instr %x, addr %x\n",
|
||||
CPNum, instr, address);
|
||||
*/
|
||||
CPTAKEABORT;
|
||||
return;
|
||||
}
|
||||
#ifndef MODE32
|
||||
if (ADDREXCEPT (address) || VECTORACCESS (address))
|
||||
INTERNALABORT (address);
|
||||
#endif
|
||||
BUSUSEDINCPCN;
|
||||
//chy 2004-05-25
|
||||
/*
|
||||
if (BIT (21))
|
||||
LSBase = state->Base;
|
||||
*/
|
||||
cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data);
|
||||
ARMul_StoreWordN (state, address, data);
|
||||
//chy 2004-05-25
|
||||
if (state->abortSig || state->Aborted)
|
||||
goto L_stc_takeabort;
|
||||
|
||||
while (cpab == ARMul_INC) {
|
||||
address += 4;
|
||||
cpab = (state->STC[CPNum]) (state, ARMul_DATA, instr, &data);
|
||||
ARMul_StoreWordN (state, address, data);
|
||||
//chy 2004-05-25
|
||||
if (state->abortSig || state->Aborted)
|
||||
goto L_stc_takeabort;
|
||||
}
|
||||
//chy 2004-05-25
|
||||
L_stc_takeabort:
|
||||
if (BIT (21)) {
|
||||
if (!
|
||||
((state->abortSig || state->Aborted)
|
||||
&& state->lateabtSig == LOW))
|
||||
LSBase = state->Base;
|
||||
}
|
||||
|
||||
if (state->abortSig || state->Aborted)
|
||||
TAKEABORT;
|
||||
}
|
||||
|
||||
/* This function does the Busy-Waiting for an MCR instruction. */
|
||||
|
||||
void
|
||||
ARMul_MCR (ARMul_State * state, ARMword instr, ARMword source)
|
||||
{
|
||||
unsigned cpab;
|
||||
|
||||
//printf("SKYEYE ARMul_MCR, CPnum is %x, source %x\n",CPNum, source);
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
//chy 2004-07-19 should fix in the future ????!!!!
|
||||
//printf("SKYEYE ARMul_MCR, ACCESS_not ALLOWed, UndefinedInstr CPnum is %x, source %x\n",CPNum, source);
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return;
|
||||
}
|
||||
|
||||
cpab = (state->MCR[CPNum]) (state, ARMul_FIRST, instr, source);
|
||||
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->MCR[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
cpab = (state->MCR[CPNum]) (state, ARMul_BUSY, instr,
|
||||
source);
|
||||
}
|
||||
|
||||
if (cpab == ARMul_CANT) {
|
||||
printf ("SKYEYE ARMul_MCR, CANT, UndefinedInstr %x CPnum is %x, source %x\n", instr, CPNum, source);
|
||||
ARMul_Abort (state, ARMul_UndefinedInstrV);
|
||||
}
|
||||
else {
|
||||
BUSUSEDINCPCN;
|
||||
ARMul_Ccycles (state, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function does the Busy-Waiting for an MCRR instruction. */
|
||||
|
||||
void
|
||||
ARMul_MCRR (ARMul_State * state, ARMword instr, ARMword source1, ARMword source2)
|
||||
{
|
||||
unsigned cpab;
|
||||
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return;
|
||||
}
|
||||
|
||||
cpab = (state->MCRR[CPNum]) (state, ARMul_FIRST, instr, source1, source2);
|
||||
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->MCRR[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr, 0, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
cpab = (state->MCRR[CPNum]) (state, ARMul_BUSY, instr,
|
||||
source1, source2);
|
||||
}
|
||||
if (cpab == ARMul_CANT) {
|
||||
printf ("In %s, CoProcesscor returned CANT, CPnum is %x, instr %x, source %x %x\n", __FUNCTION__, CPNum, instr, source1, source2);
|
||||
ARMul_Abort (state, ARMul_UndefinedInstrV);
|
||||
}
|
||||
else {
|
||||
BUSUSEDINCPCN;
|
||||
ARMul_Ccycles (state, 1, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/* This function does the Busy-Waiting for an MRC instruction. */
|
||||
|
||||
ARMword
|
||||
ARMul_MRC (ARMul_State * state, ARMword instr)
|
||||
{
|
||||
unsigned cpab;
|
||||
ARMword result = 0;
|
||||
|
||||
//printf("SKYEYE ARMul_MRC, CPnum is %x, instr %x\n",CPNum, instr);
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
//chy 2004-07-19 should fix in the future????!!!!
|
||||
//printf("SKYEYE ARMul_MRC,NOT ALLOWed UndefInstr CPnum is %x, instr %x\n",CPNum, instr);
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
cpab = (state->MRC[CPNum]) (state, ARMul_FIRST, instr, &result);
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->MRC[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr, 0);
|
||||
return (0);
|
||||
}
|
||||
else
|
||||
cpab = (state->MRC[CPNum]) (state, ARMul_BUSY, instr,
|
||||
&result);
|
||||
}
|
||||
if (cpab == ARMul_CANT) {
|
||||
printf ("SKYEYE ARMul_MRC,CANT UndefInstr CPnum is %x, instr %x\n", CPNum, instr);
|
||||
ARMul_Abort (state, ARMul_UndefinedInstrV);
|
||||
/* Parent will destroy the flags otherwise. */
|
||||
result = ECC;
|
||||
}
|
||||
else {
|
||||
BUSUSEDINCPCN;
|
||||
ARMul_Ccycles (state, 1, 0);
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* This function does the Busy-Waiting for an MRRC instruction. (to verify) */
|
||||
|
||||
void
|
||||
ARMul_MRRC (ARMul_State * state, ARMword instr, ARMword * dest1, ARMword * dest2)
|
||||
{
|
||||
unsigned cpab;
|
||||
ARMword result1 = 0;
|
||||
ARMword result2 = 0;
|
||||
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return;
|
||||
}
|
||||
|
||||
cpab = (state->MRRC[CPNum]) (state, ARMul_FIRST, instr, &result1, &result2);
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->MRRC[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr, 0, 0);
|
||||
return;
|
||||
}
|
||||
else
|
||||
cpab = (state->MRRC[CPNum]) (state, ARMul_BUSY, instr,
|
||||
&result1, &result2);
|
||||
}
|
||||
if (cpab == ARMul_CANT) {
|
||||
printf ("In %s, CoProcesscor returned CANT, CPnum is %x, instr %x\n", __FUNCTION__, CPNum, instr);
|
||||
ARMul_Abort (state, ARMul_UndefinedInstrV);
|
||||
}
|
||||
else {
|
||||
BUSUSEDINCPCN;
|
||||
ARMul_Ccycles (state, 1, 0);
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
}
|
||||
|
||||
*dest1 = result1;
|
||||
*dest2 = result2;
|
||||
}
|
||||
|
||||
/* This function does the Busy-Waiting for an CDP instruction. */
|
||||
|
||||
void
|
||||
ARMul_CDP (ARMul_State * state, ARMword instr)
|
||||
{
|
||||
unsigned cpab;
|
||||
|
||||
if (!CP_ACCESS_ALLOWED (state, CPNum)) {
|
||||
ARMul_UndefInstr (state, instr);
|
||||
return;
|
||||
}
|
||||
cpab = (state->CDP[CPNum]) (state, ARMul_FIRST, instr);
|
||||
while (cpab == ARMul_BUSY) {
|
||||
ARMul_Icycles (state, 1, 0);
|
||||
if (IntPending (state)) {
|
||||
cpab = (state->CDP[CPNum]) (state, ARMul_INTERRUPT,
|
||||
instr);
|
||||
return;
|
||||
}
|
||||
else
|
||||
cpab = (state->CDP[CPNum]) (state, ARMul_BUSY, instr);
|
||||
}
|
||||
if (cpab == ARMul_CANT)
|
||||
ARMul_Abort (state, ARMul_UndefinedInstrV);
|
||||
else
|
||||
BUSUSEDN;
|
||||
}
|
||||
|
||||
/* This function handles Undefined instructions, as CP isntruction. */
|
||||
|
||||
void
|
||||
ARMul_UndefInstr (ARMul_State * state, ARMword instr)
|
||||
{
|
||||
ERROR_LOG(ARM11, "Undefined instruction!! Instr: 0x%x", instr);
|
||||
ARMul_Abort (state, ARMul_UndefinedInstrV);
|
||||
}
|
||||
|
||||
/* Return TRUE if an interrupt is pending, FALSE otherwise. */
|
||||
|
||||
unsigned
|
||||
IntPending (ARMul_State * state)
|
||||
{
|
||||
/* Any exceptions. */
|
||||
if (state->NresetSig == LOW) {
|
||||
ARMul_Abort (state, ARMul_ResetV);
|
||||
return TRUE;
|
||||
}
|
||||
else if (!state->NfiqSig && !FFLAG) {
|
||||
ARMul_Abort (state, ARMul_FIQV);
|
||||
return TRUE;
|
||||
}
|
||||
else if (!state->NirqSig && !IFLAG) {
|
||||
ARMul_Abort (state, ARMul_IRQV);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
/* Align a word access to a non word boundary. */
|
||||
|
||||
ARMword
|
||||
ARMul_Align (ARMul_State *state, ARMword address, ARMword data)
|
||||
{
|
||||
/* This code assumes the address is really unaligned,
|
||||
as a shift by 32 is undefined in C. */
|
||||
|
||||
address = (address & 3) << 3; /* Get the word address. */
|
||||
return ((data >> address) | (data << (32 - address))); /* rot right */
|
||||
}
|
||||
|
||||
/* This routine is used to call another routine after a certain number of
|
||||
cycles have been executed. The first parameter is the number of cycles
|
||||
delay before the function is called, the second argument is a pointer
|
||||
to the function. A delay of zero doesn't work, just call the function. */
|
||||
|
||||
void
|
||||
ARMul_ScheduleEvent (ARMul_State * state, unsigned int delay,
|
||||
unsigned (*what) (ARMul_State *))
|
||||
{
|
||||
unsigned int when;
|
||||
struct EventNode *event;
|
||||
|
||||
if (state->EventSet++ == 0)
|
||||
state->Now = ARMul_Time (state);
|
||||
when = (state->Now + delay) % EVENTLISTSIZE;
|
||||
event = (struct EventNode *) malloc (sizeof (struct EventNode));
|
||||
|
||||
_dbg_assert_msg_(ARM11, event, "SKYEYE:ARMul_ScheduleEvent: malloc event error\n");
|
||||
|
||||
event->func = what;
|
||||
event->next = *(state->EventPtr + when);
|
||||
*(state->EventPtr + when) = event;
|
||||
}
|
||||
|
||||
/* This routine is called at the beginning of
|
||||
every cycle, to envoke scheduled events. */
|
||||
|
||||
void
|
||||
ARMul_EnvokeEvent (ARMul_State * state)
|
||||
{
|
||||
static unsigned int then;
|
||||
|
||||
then = state->Now;
|
||||
state->Now = ARMul_Time (state) % EVENTLISTSIZE;
|
||||
if (then < state->Now)
|
||||
/* Schedule events. */
|
||||
EnvokeList (state, then, state->Now);
|
||||
else if (then > state->Now) {
|
||||
/* Need to wrap around the list. */
|
||||
EnvokeList (state, then, EVENTLISTSIZE - 1L);
|
||||
EnvokeList (state, 0L, state->Now);
|
||||
}
|
||||
}
|
||||
|
||||
/* Envokes all the entries in a range. */
|
||||
|
||||
static void
|
||||
EnvokeList (ARMul_State * state, unsigned int from, unsigned int to)
|
||||
{
|
||||
for (; from <= to; from++) {
|
||||
struct EventNode *anevent;
|
||||
|
||||
anevent = *(state->EventPtr + from);
|
||||
while (anevent) {
|
||||
(anevent->func) (state);
|
||||
state->EventSet--;
|
||||
anevent = anevent->next;
|
||||
}
|
||||
*(state->EventPtr + from) = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* This routine is returns the number of clock ticks since the last reset. */
|
||||
|
||||
unsigned int
|
||||
ARMul_Time (ARMul_State * state)
|
||||
{
|
||||
return (state->NumScycles + state->NumNcycles +
|
||||
state->NumIcycles + state->NumCcycles + state->NumFcycles);
|
||||
}
|
680
src/core/src/arm/armvirt.cpp
Normal file
680
src/core/src/arm/armvirt.cpp
Normal file
@ -0,0 +1,680 @@
|
||||
/* armvirt.c -- ARMulator virtual memory interace: ARM6 Instruction Emulator.
|
||||
Copyright (C) 1994 Advanced RISC Machines Ltd.
|
||||
|
||||
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; either version 2 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program; if not, write to the Free Software
|
||||
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
|
||||
|
||||
/* This file contains a complete ARMulator memory model, modelling a
|
||||
"virtual memory" system. A much simpler model can be found in armfast.c,
|
||||
and that model goes faster too, but has a fixed amount of memory. This
|
||||
model's memory has 64K pages, allocated on demand from a 64K entry page
|
||||
table. The routines PutWord and GetWord implement this. Pages are never
|
||||
freed as they might be needed again. A single area of memory may be
|
||||
defined to generate aborts. */
|
||||
|
||||
#include "armdefs.h"
|
||||
#include "skyeye_defs.h"
|
||||
//#include "code_cov.h"
|
||||
|
||||
#ifdef VALIDATE /* for running the validate suite */
|
||||
#define TUBE 48 * 1024 * 1024 /* write a char on the screen */
|
||||
#define ABORTS 1
|
||||
#endif
|
||||
|
||||
/* #define ABORTS */
|
||||
|
||||
#ifdef ABORTS /* the memory system will abort */
|
||||
/* For the old test suite Abort between 32 Kbytes and 32 Mbytes
|
||||
For the new test suite Abort between 8 Mbytes and 26 Mbytes */
|
||||
/* #define LOWABORT 32 * 1024
|
||||
#define HIGHABORT 32 * 1024 * 1024 */
|
||||
#define LOWABORT 8 * 1024 * 1024
|
||||
#define HIGHABORT 26 * 1024 * 1024
|
||||
|
||||
#endif
|
||||
|
||||
#define NUMPAGES 64 * 1024
|
||||
#define PAGESIZE 64 * 1024
|
||||
#define PAGEBITS 16
|
||||
#define OFFSETBITS 0xffff
|
||||
//chy 2003-08-19: seems no use ????
|
||||
int SWI_vector_installed = FALSE;
|
||||
extern ARMword skyeye_cachetype;
|
||||
|
||||
/***************************************************************************\
|
||||
* Get a byte into Virtual Memory, maybe allocating the page *
|
||||
\***************************************************************************/
|
||||
static fault_t
|
||||
GetByte (ARMul_State * state, ARMword address, ARMword * data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
fault = mmu_read_byte (state, address, data);
|
||||
if (fault) {
|
||||
//chy 2003-07-11: sometime has fault, but linux can continue running !!!!????
|
||||
// printf("SKYEYE: GetByte fault %d \n", fault);
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Get a halfword into Virtual Memory, maybe allocating the page *
|
||||
\***************************************************************************/
|
||||
static fault_t
|
||||
GetHalfWord (ARMul_State * state, ARMword address, ARMword * data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
fault = mmu_read_halfword (state, address, data);
|
||||
if (fault) {
|
||||
//chy 2003-07-11: sometime has fault, but linux can continue running !!!!????
|
||||
// printf("SKYEYE: GetHalfWord fault %d \n", fault);
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Get a Word from Virtual Memory, maybe allocating the page *
|
||||
\***************************************************************************/
|
||||
|
||||
static fault_t
|
||||
GetWord (ARMul_State * state, ARMword address, ARMword * data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
fault = mmu_read_word (state, address, data);
|
||||
if (fault) {
|
||||
//chy 2003-07-11: sometime has fault, but linux can continue running !!!!????
|
||||
#if 0
|
||||
/* XXX */ extern int hack;
|
||||
hack = 1;
|
||||
#endif
|
||||
#if 0
|
||||
printf ("mmu_read_word at 0x%08x: ", address);
|
||||
switch (fault) {
|
||||
case ALIGNMENT_FAULT:
|
||||
printf ("ALIGNMENT_FAULT");
|
||||
break;
|
||||
case SECTION_TRANSLATION_FAULT:
|
||||
printf ("SECTION_TRANSLATION_FAULT");
|
||||
break;
|
||||
case PAGE_TRANSLATION_FAULT:
|
||||
printf ("PAGE_TRANSLATION_FAULT");
|
||||
break;
|
||||
case SECTION_DOMAIN_FAULT:
|
||||
printf ("SECTION_DOMAIN_FAULT");
|
||||
break;
|
||||
case SECTION_PERMISSION_FAULT:
|
||||
printf ("SECTION_PERMISSION_FAULT");
|
||||
break;
|
||||
case SUBPAGE_PERMISSION_FAULT:
|
||||
printf ("SUBPAGE_PERMISSION_FAULT");
|
||||
break;
|
||||
default:
|
||||
printf ("Unrecognized fault number!");
|
||||
}
|
||||
printf ("\tpc = 0x%08x\n", state->Reg[15]);
|
||||
#endif
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
//2003-07-10 chy: lyh change
|
||||
/****************************************************************************\
|
||||
* Load a Instrion Word into Virtual Memory *
|
||||
\****************************************************************************/
|
||||
static fault_t
|
||||
LoadInstr (ARMul_State * state, ARMword address, ARMword * instr)
|
||||
{
|
||||
fault_t fault;
|
||||
fault = mmu_load_instr (state, address, instr);
|
||||
return fault;
|
||||
//if (fault)
|
||||
// log_msg("load_instr fault = %d, address = %x\n", fault, address);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Put a byte into Virtual Memory, maybe allocating the page *
|
||||
\***************************************************************************/
|
||||
static fault_t
|
||||
PutByte (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
fault = mmu_write_byte (state, address, data);
|
||||
if (fault) {
|
||||
//chy 2003-07-11: sometime has fault, but linux can continue running !!!!????
|
||||
// printf("SKYEYE: PutByte fault %d \n", fault);
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Put a halfword into Virtual Memory, maybe allocating the page *
|
||||
\***************************************************************************/
|
||||
static fault_t
|
||||
PutHalfWord (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
fault = mmu_write_halfword (state, address, data);
|
||||
if (fault) {
|
||||
//chy 2003-07-11: sometime has fault, but linux can continue running !!!!????
|
||||
// printf("SKYEYE: PutHalfWord fault %d \n", fault);
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Put a Word into Virtual Memory, maybe allocating the page *
|
||||
\***************************************************************************/
|
||||
|
||||
static fault_t
|
||||
PutWord (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
fault = mmu_write_word (state, address, data);
|
||||
if (fault) {
|
||||
//chy 2003-07-11: sometime has fault, but linux can continue running !!!!????
|
||||
#if 0
|
||||
/* XXX */ extern int hack;
|
||||
hack = 1;
|
||||
#endif
|
||||
#if 0
|
||||
printf ("mmu_write_word at 0x%08x: ", address);
|
||||
switch (fault) {
|
||||
case ALIGNMENT_FAULT:
|
||||
printf ("ALIGNMENT_FAULT");
|
||||
break;
|
||||
case SECTION_TRANSLATION_FAULT:
|
||||
printf ("SECTION_TRANSLATION_FAULT");
|
||||
break;
|
||||
case PAGE_TRANSLATION_FAULT:
|
||||
printf ("PAGE_TRANSLATION_FAULT");
|
||||
break;
|
||||
case SECTION_DOMAIN_FAULT:
|
||||
printf ("SECTION_DOMAIN_FAULT");
|
||||
break;
|
||||
case SECTION_PERMISSION_FAULT:
|
||||
printf ("SECTION_PERMISSION_FAULT");
|
||||
break;
|
||||
case SUBPAGE_PERMISSION_FAULT:
|
||||
printf ("SUBPAGE_PERMISSION_FAULT");
|
||||
break;
|
||||
default:
|
||||
printf ("Unrecognized fault number!");
|
||||
}
|
||||
printf ("\tpc = 0x%08x\n", state->Reg[15]);
|
||||
#endif
|
||||
}
|
||||
return fault;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Initialise the memory interface *
|
||||
\***************************************************************************/
|
||||
|
||||
unsigned
|
||||
ARMul_MemoryInit (ARMul_State * state, unsigned int initmemsize)
|
||||
{
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Remove the memory interface *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_MemoryExit (ARMul_State * state)
|
||||
{
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* ReLoad Instruction *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_ReLoadInstr (ARMul_State * state, ARMword address, ARMword isize)
|
||||
{
|
||||
ARMword data;
|
||||
fault_t fault;
|
||||
|
||||
#ifdef ABORTS
|
||||
if (address >= LOWABORT && address < HIGHABORT) {
|
||||
ARMul_PREFETCHABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
#endif
|
||||
#if 0
|
||||
/* do profiling for code coverage */
|
||||
if (skyeye_config.code_cov.prof_on)
|
||||
cov_prof(EXEC_FLAG, address);
|
||||
#endif
|
||||
#if 1
|
||||
if ((isize == 2) && (address & 0x2)) {
|
||||
ARMword lo, hi;
|
||||
if (!(skyeye_cachetype == INSTCACHE))
|
||||
fault = GetHalfWord (state, address, &lo);
|
||||
else
|
||||
fault = LoadInstr (state, address, &lo);
|
||||
#if 0
|
||||
if (!fault) {
|
||||
if (!(skyeye_cachetype == INSTCACHE))
|
||||
fault = GetHalfWord (state, address + isize, &hi);
|
||||
else
|
||||
fault = LoadInstr (state, address + isize, &hi);
|
||||
|
||||
}
|
||||
#endif
|
||||
if (fault) {
|
||||
ARMul_PREFETCHABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
return lo;
|
||||
#if 0
|
||||
if (state->bigendSig == HIGH)
|
||||
return (lo << 16) | (hi >> 16);
|
||||
else
|
||||
return ((hi & 0xFFFF) << 16) | (lo >> 16);
|
||||
#endif
|
||||
}
|
||||
#endif
|
||||
if (!(skyeye_cachetype == INSTCACHE))
|
||||
fault = GetWord (state, address, &data);
|
||||
else
|
||||
fault = LoadInstr (state, address, &data);
|
||||
|
||||
if (fault) {
|
||||
|
||||
/* dyf add for s3c6410 no instcache temporary 2010.9.17 */
|
||||
if (!(skyeye_cachetype == INSTCACHE)) {
|
||||
/* set translation fault on prefetch abort */
|
||||
state->mmu.fault_statusi = fault & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
}
|
||||
/* add end */
|
||||
|
||||
ARMul_PREFETCHABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Load Instruction, Sequential Cycle *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_LoadInstrS (ARMul_State * state, ARMword address, ARMword isize)
|
||||
{
|
||||
state->NumScycles++;
|
||||
|
||||
#ifdef HOURGLASS
|
||||
if ((state->NumScycles & HOURGLASS_RATE) == 0) {
|
||||
HOURGLASS;
|
||||
}
|
||||
#endif
|
||||
|
||||
return ARMul_ReLoadInstr (state, address, isize);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Load Instruction, Non Sequential Cycle *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_LoadInstrN (ARMul_State * state, ARMword address, ARMword isize)
|
||||
{
|
||||
state->NumNcycles++;
|
||||
|
||||
return ARMul_ReLoadInstr (state, address, isize);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Read Word (but don't tell anyone!) *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_ReadWord (ARMul_State * state, ARMword address)
|
||||
{
|
||||
ARMword data;
|
||||
fault_t fault;
|
||||
|
||||
#ifdef ABORTS
|
||||
if (address >= LOWABORT && address < HIGHABORT) {
|
||||
ARMul_DATAABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
fault = GetWord (state, address, &data);
|
||||
if (fault) {
|
||||
state->mmu.fault_status =
|
||||
(fault | (state->mmu.last_domain << 4)) & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
ARMul_DATAABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Load Word, Sequential Cycle *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_LoadWordS (ARMul_State * state, ARMword address)
|
||||
{
|
||||
state->NumScycles++;
|
||||
|
||||
return ARMul_ReadWord (state, address);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Load Word, Non Sequential Cycle *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_LoadWordN (ARMul_State * state, ARMword address)
|
||||
{
|
||||
state->NumNcycles++;
|
||||
|
||||
return ARMul_ReadWord (state, address);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Load Halfword, (Non Sequential Cycle) *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_LoadHalfWord (ARMul_State * state, ARMword address)
|
||||
{
|
||||
ARMword data;
|
||||
fault_t fault;
|
||||
|
||||
state->NumNcycles++;
|
||||
fault = GetHalfWord (state, address, &data);
|
||||
|
||||
if (fault) {
|
||||
state->mmu.fault_status =
|
||||
(fault | (state->mmu.last_domain << 4)) & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
ARMul_DATAABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Read Byte (but don't tell anyone!) *
|
||||
\***************************************************************************/
|
||||
int ARMul_ICE_ReadByte(ARMul_State * state, ARMword address, ARMword *presult)
|
||||
{
|
||||
ARMword data;
|
||||
fault_t fault;
|
||||
fault = GetByte (state, address, &data);
|
||||
if (fault) {
|
||||
*presult=-1; fault=ALIGNMENT_FAULT; return fault;
|
||||
}else{
|
||||
*(char *)presult=(unsigned char)(data & 0xff); fault=NO_FAULT; return fault;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ARMword
|
||||
ARMul_ReadByte (ARMul_State * state, ARMword address)
|
||||
{
|
||||
ARMword data;
|
||||
fault_t fault;
|
||||
|
||||
fault = GetByte (state, address, &data);
|
||||
|
||||
if (fault) {
|
||||
state->mmu.fault_status =
|
||||
(fault | (state->mmu.last_domain << 4)) & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
ARMul_DATAABORT (address);
|
||||
return ARMul_ABORTWORD;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
|
||||
return data;
|
||||
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Load Byte, (Non Sequential Cycle) *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_LoadByte (ARMul_State * state, ARMword address)
|
||||
{
|
||||
state->NumNcycles++;
|
||||
|
||||
return ARMul_ReadByte (state, address);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Write Word (but don't tell anyone!) *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_WriteWord (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
|
||||
#ifdef ABORTS
|
||||
if (address >= LOWABORT && address < HIGHABORT) {
|
||||
ARMul_DATAABORT (address);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
#endif
|
||||
|
||||
fault = PutWord (state, address, data);
|
||||
if (fault) {
|
||||
state->mmu.fault_status =
|
||||
(fault | (state->mmu.last_domain << 4)) & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
ARMul_DATAABORT (address);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Store Word, Sequential Cycle *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_StoreWordS (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
state->NumScycles++;
|
||||
|
||||
ARMul_WriteWord (state, address, data);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Store Word, Non Sequential Cycle *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_StoreWordN (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
state->NumNcycles++;
|
||||
|
||||
ARMul_WriteWord (state, address, data);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Store HalfWord, (Non Sequential Cycle) *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_StoreHalfWord (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
state->NumNcycles++;
|
||||
fault = PutHalfWord (state, address, data);
|
||||
if (fault) {
|
||||
state->mmu.fault_status =
|
||||
(fault | (state->mmu.last_domain << 4)) & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
ARMul_DATAABORT (address);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
}
|
||||
|
||||
//chy 2006-04-15
|
||||
int ARMul_ICE_WriteByte (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
fault = PutByte (state, address, data);
|
||||
if (fault)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
/***************************************************************************\
|
||||
* Write Byte (but don't tell anyone!) *
|
||||
\***************************************************************************/
|
||||
//chy 2003-07-10, add real write byte fun
|
||||
void
|
||||
ARMul_WriteByte (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
fault_t fault;
|
||||
fault = PutByte (state, address, data);
|
||||
if (fault) {
|
||||
state->mmu.fault_status =
|
||||
(fault | (state->mmu.last_domain << 4)) & 0xFF;
|
||||
state->mmu.fault_address = address;
|
||||
ARMul_DATAABORT (address);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Store Byte, (Non Sequential Cycle) *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_StoreByte (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
state->NumNcycles++;
|
||||
|
||||
#ifdef VALIDATE
|
||||
if (address == TUBE) {
|
||||
if (data == 4)
|
||||
state->Emulate = FALSE;
|
||||
else
|
||||
(void) putc ((char) data, stderr); /* Write Char */
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
ARMul_WriteByte (state, address, data);
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Swap Word, (Two Non Sequential Cycles) *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_SwapWord (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
ARMword temp;
|
||||
|
||||
state->NumNcycles++;
|
||||
|
||||
temp = ARMul_ReadWord (state, address);
|
||||
|
||||
state->NumNcycles++;
|
||||
|
||||
PutWord (state, address, data);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Swap Byte, (Two Non Sequential Cycles) *
|
||||
\***************************************************************************/
|
||||
|
||||
ARMword
|
||||
ARMul_SwapByte (ARMul_State * state, ARMword address, ARMword data)
|
||||
{
|
||||
ARMword temp;
|
||||
|
||||
temp = ARMul_LoadByte (state, address);
|
||||
ARMul_StoreByte (state, address, data);
|
||||
|
||||
return temp;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Count I Cycles *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_Icycles (ARMul_State * state, unsigned number,
|
||||
ARMword address)
|
||||
{
|
||||
state->NumIcycles += number;
|
||||
ARMul_CLEARABORT;
|
||||
}
|
||||
|
||||
/***************************************************************************\
|
||||
* Count C Cycles *
|
||||
\***************************************************************************/
|
||||
|
||||
void
|
||||
ARMul_Ccycles (ARMul_State * state, unsigned number,
|
||||
ARMword address)
|
||||
{
|
||||
state->NumCcycles += number;
|
||||
ARMul_CLEARABORT;
|
||||
}
|
Loading…
Reference in New Issue
Block a user