target/m68k: manage 680x0 stack frames
680x0 manages several stack frame formats: - format 0: four-word stack frame - format 1: four-word throwaway stack frame - format 2: six-word stack frame - format 3: Floating-Point post-instruction stack frame - format 4: eight-word stack frame - format 7: access-error stack frame Signed-off-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Richard Henderson <richard.henderson@linaro.org> Message-Id: <20180104012913.30763-7-laurent@vivier.eu>
This commit is contained in:
		
							parent
							
								
									5beb144e04
								
							
						
					
					
						commit
						d2f8fb8e7f
					
				| 
						 | 
				
			
			@ -178,6 +178,7 @@ int cpu_m68k_signal_handler(int host_signum, void *pinfo,
 | 
			
		|||
                           void *puc);
 | 
			
		||||
uint32_t cpu_m68k_get_ccr(CPUM68KState *env);
 | 
			
		||||
void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t);
 | 
			
		||||
void cpu_m68k_set_sr(CPUM68KState *env, uint32_t);
 | 
			
		||||
void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -316,13 +316,17 @@ uint32_t HELPER(sats)(uint32_t val, uint32_t v)
 | 
			
		|||
    return val;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
 | 
			
		||||
void cpu_m68k_set_sr(CPUM68KState *env, uint32_t sr)
 | 
			
		||||
{
 | 
			
		||||
    env->sr = val & 0xffe0;
 | 
			
		||||
    cpu_m68k_set_ccr(env, val);
 | 
			
		||||
    env->sr = sr & 0xffe0;
 | 
			
		||||
    cpu_m68k_set_ccr(env, sr);
 | 
			
		||||
    m68k_switch_sp(env);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
 | 
			
		||||
{
 | 
			
		||||
    cpu_m68k_set_sr(env, val);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/* MAC unit.  */
 | 
			
		||||
/* FIXME: The MAC unit implementation is a bit of a mess.  Some helpers
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -54,7 +54,7 @@ void tlb_fill(CPUState *cs, target_ulong addr, MMUAccessType access_type,
 | 
			
		|||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_rte(CPUM68KState *env)
 | 
			
		||||
static void cf_rte(CPUM68KState *env)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t sp;
 | 
			
		||||
    uint32_t fmt;
 | 
			
		||||
| 
						 | 
				
			
			@ -65,7 +65,46 @@ static void do_rte(CPUM68KState *env)
 | 
			
		|||
    sp |= (fmt >> 28) & 3;
 | 
			
		||||
    env->aregs[7] = sp + 8;
 | 
			
		||||
 | 
			
		||||
    helper_set_sr(env, fmt);
 | 
			
		||||
    cpu_m68k_set_sr(env, fmt);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void m68k_rte(CPUM68KState *env)
 | 
			
		||||
{
 | 
			
		||||
    uint32_t sp;
 | 
			
		||||
    uint16_t fmt;
 | 
			
		||||
    uint16_t sr;
 | 
			
		||||
 | 
			
		||||
    sp = env->aregs[7];
 | 
			
		||||
throwaway:
 | 
			
		||||
    sr = cpu_lduw_kernel(env, sp);
 | 
			
		||||
    sp += 2;
 | 
			
		||||
    env->pc = cpu_ldl_kernel(env, sp);
 | 
			
		||||
    sp += 4;
 | 
			
		||||
    if (m68k_feature(env, M68K_FEATURE_QUAD_MULDIV)) {
 | 
			
		||||
        /*  all except 68000 */
 | 
			
		||||
        fmt = cpu_lduw_kernel(env, sp);
 | 
			
		||||
        sp += 2;
 | 
			
		||||
        switch (fmt >> 12) {
 | 
			
		||||
        case 0:
 | 
			
		||||
            break;
 | 
			
		||||
        case 1:
 | 
			
		||||
            env->aregs[7] = sp;
 | 
			
		||||
            cpu_m68k_set_sr(env, sr);
 | 
			
		||||
            goto throwaway;
 | 
			
		||||
        case 2:
 | 
			
		||||
        case 3:
 | 
			
		||||
            sp += 4;
 | 
			
		||||
            break;
 | 
			
		||||
        case 4:
 | 
			
		||||
            sp += 8;
 | 
			
		||||
            break;
 | 
			
		||||
        case 7:
 | 
			
		||||
            sp += 52;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    env->aregs[7] = sp;
 | 
			
		||||
    cpu_m68k_set_sr(env, sr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static const char *m68k_exception_name(int index)
 | 
			
		||||
| 
						 | 
				
			
			@ -173,7 +212,7 @@ static const char *m68k_exception_name(int index)
 | 
			
		|||
    return "Unassigned";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_interrupt_all(CPUM68KState *env, int is_hw)
 | 
			
		||||
static void cf_interrupt_all(CPUM68KState *env, int is_hw)
 | 
			
		||||
{
 | 
			
		||||
    CPUState *cs = CPU(m68k_env_get_cpu(env));
 | 
			
		||||
    uint32_t sp;
 | 
			
		||||
| 
						 | 
				
			
			@ -189,7 +228,7 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
 | 
			
		|||
        switch (cs->exception_index) {
 | 
			
		||||
        case EXCP_RTE:
 | 
			
		||||
            /* Return from an exception.  */
 | 
			
		||||
            do_rte(env);
 | 
			
		||||
            cf_rte(env);
 | 
			
		||||
            return;
 | 
			
		||||
        case EXCP_HALT_INSN:
 | 
			
		||||
            if (semihosting_enabled()
 | 
			
		||||
| 
						 | 
				
			
			@ -247,6 +286,119 @@ static void do_interrupt_all(CPUM68KState *env, int is_hw)
 | 
			
		|||
    env->pc = cpu_ldl_kernel(env, env->vbr + vector);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static inline void do_stack_frame(CPUM68KState *env, uint32_t *sp,
 | 
			
		||||
                                  uint16_t format, uint16_t sr,
 | 
			
		||||
                                  uint32_t addr, uint32_t retaddr)
 | 
			
		||||
{
 | 
			
		||||
    CPUState *cs = CPU(m68k_env_get_cpu(env));
 | 
			
		||||
    switch (format) {
 | 
			
		||||
    case 4:
 | 
			
		||||
        *sp -= 4;
 | 
			
		||||
        cpu_stl_kernel(env, *sp, env->pc);
 | 
			
		||||
        *sp -= 4;
 | 
			
		||||
        cpu_stl_kernel(env, *sp, addr);
 | 
			
		||||
        break;
 | 
			
		||||
    case 3:
 | 
			
		||||
    case 2:
 | 
			
		||||
        *sp -= 4;
 | 
			
		||||
        cpu_stl_kernel(env, *sp, addr);
 | 
			
		||||
        break;
 | 
			
		||||
    }
 | 
			
		||||
    *sp -= 2;
 | 
			
		||||
    cpu_stw_kernel(env, *sp, (format << 12) + (cs->exception_index << 2));
 | 
			
		||||
    *sp -= 4;
 | 
			
		||||
    cpu_stl_kernel(env, *sp, retaddr);
 | 
			
		||||
    *sp -= 2;
 | 
			
		||||
    cpu_stw_kernel(env, *sp, sr);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void m68k_interrupt_all(CPUM68KState *env, int is_hw)
 | 
			
		||||
{
 | 
			
		||||
    CPUState *cs = CPU(m68k_env_get_cpu(env));
 | 
			
		||||
    uint32_t sp;
 | 
			
		||||
    uint32_t retaddr;
 | 
			
		||||
    uint32_t vector;
 | 
			
		||||
    uint16_t sr, oldsr;
 | 
			
		||||
 | 
			
		||||
    retaddr = env->pc;
 | 
			
		||||
 | 
			
		||||
    if (!is_hw) {
 | 
			
		||||
        switch (cs->exception_index) {
 | 
			
		||||
        case EXCP_RTE:
 | 
			
		||||
            /* Return from an exception.  */
 | 
			
		||||
            m68k_rte(env);
 | 
			
		||||
            return;
 | 
			
		||||
        case EXCP_TRAP0 ...  EXCP_TRAP15:
 | 
			
		||||
            /* Move the PC after the trap instruction.  */
 | 
			
		||||
            retaddr += 2;
 | 
			
		||||
            break;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    vector = cs->exception_index << 2;
 | 
			
		||||
 | 
			
		||||
    sr = env->sr | cpu_m68k_get_ccr(env);
 | 
			
		||||
    if (qemu_loglevel_mask(CPU_LOG_INT)) {
 | 
			
		||||
        static int count;
 | 
			
		||||
        qemu_log("INT %6d: %s(%#x) pc=%08x sp=%08x sr=%04x\n",
 | 
			
		||||
                 ++count, m68k_exception_name(cs->exception_index),
 | 
			
		||||
                 vector, env->pc, env->aregs[7], sr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /*
 | 
			
		||||
     * MC68040UM/AD,  chapter 9.3.10
 | 
			
		||||
     */
 | 
			
		||||
 | 
			
		||||
    /* "the processor first make an internal copy" */
 | 
			
		||||
    oldsr = sr;
 | 
			
		||||
    /* "set the mode to supervisor" */
 | 
			
		||||
    sr |= SR_S;
 | 
			
		||||
    /* "suppress tracing" */
 | 
			
		||||
    sr &= ~SR_T;
 | 
			
		||||
    /* "sets the processor interrupt mask" */
 | 
			
		||||
    if (is_hw) {
 | 
			
		||||
        sr |= (env->sr & ~SR_I) | (env->pending_level << SR_I_SHIFT);
 | 
			
		||||
    }
 | 
			
		||||
    cpu_m68k_set_sr(env, sr);
 | 
			
		||||
    sp = env->aregs[7];
 | 
			
		||||
 | 
			
		||||
    sp &= ~1;
 | 
			
		||||
    if (cs->exception_index == EXCP_ADDRESS) {
 | 
			
		||||
        do_stack_frame(env, &sp, 2, oldsr, 0, retaddr);
 | 
			
		||||
    } else if (cs->exception_index == EXCP_ILLEGAL ||
 | 
			
		||||
               cs->exception_index == EXCP_DIV0 ||
 | 
			
		||||
               cs->exception_index == EXCP_CHK ||
 | 
			
		||||
               cs->exception_index == EXCP_TRAPCC ||
 | 
			
		||||
               cs->exception_index == EXCP_TRACE) {
 | 
			
		||||
        /* FIXME: addr is not only env->pc */
 | 
			
		||||
        do_stack_frame(env, &sp, 2, oldsr, env->pc, retaddr);
 | 
			
		||||
    } else if (is_hw && oldsr & SR_M &&
 | 
			
		||||
               cs->exception_index >= EXCP_SPURIOUS &&
 | 
			
		||||
               cs->exception_index <= EXCP_INT_LEVEL_7) {
 | 
			
		||||
        do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
 | 
			
		||||
        oldsr = sr;
 | 
			
		||||
        env->aregs[7] = sp;
 | 
			
		||||
        cpu_m68k_set_sr(env, sr &= ~SR_M);
 | 
			
		||||
        sp = env->aregs[7] & ~1;
 | 
			
		||||
        do_stack_frame(env, &sp, 1, oldsr, 0, retaddr);
 | 
			
		||||
    } else {
 | 
			
		||||
        do_stack_frame(env, &sp, 0, oldsr, 0, retaddr);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    env->aregs[7] = sp;
 | 
			
		||||
    /* Jump to vector.  */
 | 
			
		||||
    env->pc = cpu_ldl_kernel(env, env->vbr + vector);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
static void do_interrupt_all(CPUM68KState *env, int is_hw)
 | 
			
		||||
{
 | 
			
		||||
    if (m68k_feature(env, M68K_FEATURE_M68000)) {
 | 
			
		||||
        m68k_interrupt_all(env, is_hw);
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    cf_interrupt_all(env, is_hw);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
void m68k_cpu_do_interrupt(CPUState *cs)
 | 
			
		||||
{
 | 
			
		||||
    M68kCPU *cpu = M68K_CPU(cs);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue