OpenCores
URL https://opencores.org/ocsvn/aor3000/aor3000/trunk

Subversion Repositories aor3000

[/] [aor3000/] [trunk/] [sim/] [vmips/] [vmips_emulator.cpp] - Rev 2

Compare with Previous | Blame | View Log

/*
 * This file is subject to the terms and conditions of the GPL License. See
 * the file "LICENSE" in the main directory of this archive for more details.
 *
 * Copyright (C) 2014 Aleksander Osman
 */
 
#include <cstdio>
#include <cstdlib>
#include <cstring>
 
#include "shared_mem.h"
#include "vmips_emulator.h"
 
//------------------------------------------------------------------------------ Code from vmips-1.4.1 project under the GPL license
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
 
/* MIPS R3000 CPU emulation.
   Copyright 2001, 2002, 2003, 2004 Brian R. Gaeke.
 
This file is part of VMIPS.
 
VMIPS 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.
 
VMIPS 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 VMIPS; if not, write to the Free Software Foundation, Inc.,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
 
//------------------------------------------------------------------------------ cpzero.cc
 
static uint32 read_masks[] = {
    Index_MASK, Random_MASK, EntryLo_MASK, 0, Context_MASK,
    PageMask_MASK, Wired_MASK, Error_MASK, BadVAddr_MASK, Count_MASK,
    EntryHi_MASK, Compare_MASK, Status_MASK, Cause_MASK, EPC_MASK,
    PRId_MASK, Config_MASK, LLAddr_MASK, WatchLo_MASK, WatchHi_MASK,
    0, 0, 0, 0, 0, 0, ECC_MASK, CacheErr_MASK, TagLo_MASK, TagHi_MASK,
    ErrorEPC_MASK, 0
};
 
static uint32 write_masks[] = {
    Index_MASK, 0, EntryLo_MASK, 0, Context_MASK & ~Context_BadVPN_MASK,
    PageMask_MASK, Wired_MASK, Error_MASK, 0, Count_MASK,
    EntryHi_MASK, Compare_MASK, Status_MASK,
    Cause_MASK & ~Cause_IP_Ext_MASK, 0, 0, Config_MASK, LLAddr_MASK,
    WatchLo_MASK, WatchHi_MASK, 0, 0, 0, 0, 0, 0, ECC_MASK,
    CacheErr_MASK, TagLo_MASK, TagHi_MASK, ErrorEPC_MASK, 0
};
 
CPZero::CPZero(CPU *m) : cpu (m) { }
 
/* Reset (warm or cold) */
void
CPZero::reset(void)
{
    int r;
    for (r = 0; r < 16; r++) {
        reg[r] = 0;
    }
    /* Turn off any randomly-set pending-interrupt bits, as these
     * can impact correctness. */
    reg[Cause] &= ~Cause_IP_MASK;
    /* Reset Random register to upper bound (8<=Random<=63) */
    reg[Random] = Random_UPPER_BOUND << 8;
    /* Reset Status register: clear KUc, IEc, SwC (i.e., caches are not
     * switched), TS (TLB shutdown has not occurred), and set
     * BEV (Bootstrap exception vectors ARE in effect).
     */
    reg[Status] = (reg[Status] | Status_DS_BEV_MASK) &
        ~(Status_KUc_MASK | Status_IEc_MASK | Status_DS_SwC_MASK |
          Status_DS_TS_MASK);
    reg[PRId] = 0x00000230; /* MIPS R3000A */
}
 
/* Yow!! Are we in KERNEL MODE yet?? ...Read the Status register. */
bool
CPZero::kernel_mode(void) const
{
    return !(reg[Status] & Status_KUc_MASK);
}
 
/* Request for address translation (possibly using the TLB). */
uint32
CPZero::address_trans(uint32 vaddr, int mode, bool *cacheable, bool *cache_isolated)
{
    (*cache_isolated) = caches_isolated();
 
    if (kernel_mode()) {
        switch(vaddr & KSEG_SELECT_MASK) {
        case KSEG0:
            *cacheable = true;
            return vaddr - KSEG0_CONST_TRANSLATION;
        case KSEG1:
            *cacheable = false;
            return vaddr - KSEG1_CONST_TRANSLATION;
        case KSEG2:
        case KSEG2_top:
            return tlb_translate(KSEG2, vaddr, mode, cacheable);
        default: /* KUSEG */
            return tlb_translate(KUSEG, vaddr, mode, cacheable);
        }
    }
 
    /* user mode */
    if (vaddr & KERNEL_SPACE_MASK) {
        /* Can't go there. */
        cpu->exception(mode == DATASTORE ? AdES : AdEL, mode);
        return 0xffffffff;
    } else /* user space address */ {
        return tlb_translate(KUSEG, vaddr, mode, cacheable);
    }
}
 
void
CPZero::load_addr_trans_excp_info(uint32 va, uint32 vpn, TLBEntry *match)
{
    reg[BadVAddr] = va;
    reg[Context] = (reg[Context] & ~Context_BadVPN_MASK) | ((va & 0x7ffff000) >> 10);
    reg[EntryHi] = (va & EntryHi_VPN_MASK) | (reg[EntryHi] & ~EntryHi_VPN_MASK);
}
 
int
CPZero::find_matching_tlb_entry(uint32 vpn, uint32 asid)
{
    for (uint16 x = 0; x < TLB_ENTRIES; x++)
        if (tlb[x].vpn() == vpn && (tlb[x].global() || tlb[x].asid() == asid))
            return x;
    return -1;
}
 
uint32
CPZero::tlb_translate(uint32 seg, uint32 vaddr, int mode, bool *cacheable)
{
    uint32 asid = reg[EntryHi] & EntryHi_ASID_MASK;
    uint32 vpn = vaddr & EntryHi_VPN_MASK;
    int index = find_matching_tlb_entry(vpn, asid);
    TLBEntry *match = (index == -1) ? 0 : &tlb[index];
    tlb_miss_user = false;
    if (match && match->valid()) {
        if (mode == DATASTORE && !match->dirty()) {
            /* TLB Mod exception - write to page not marked "dirty" */
            load_addr_trans_excp_info(vaddr,vpn,match);
            cpu->exception(Mod, DATASTORE);
            return 0xffffffff;
        } else {
            /* We have a matching TLB entry which is valid. */
            *cacheable = !match->noncacheable();
            return match->pfn() | (vaddr & ~EntryHi_VPN_MASK);
        }
    }
    // If we got here, then there was no matching tlb entry, or it wasn't valid.
    // Use special refill handler vector for user TLB miss.
    tlb_miss_user = (seg == KUSEG && !match);
    load_addr_trans_excp_info(vaddr,vpn,match);
    //fprintf(stderr, "TLB: Miss for vaddr=%x (vpn=%x)\n", vaddr, (vaddr>>12));
    cpu->exception(mode == DATASTORE ? TLBS : TLBL, mode);
    return 0xffffffff;
}
 
uint32 CPZero::read_reg(const uint16 r) {
    // This ensures that non-existent CP0 registers read as zero.
    return reg[r] & read_masks[r];
}
 
void CPZero::write_reg(const uint16 r, const uint32 data) {
    // This preserves the bits which are readable but not writable, and writes
    // the bits which are writable with new data, thus making it suitable
    // for mtc0-type operations.  If you want to write all the bits which
    // are _connected_, use: reg[r] = new_data & write_masks[r]; .
    reg[r] = (reg[r] & (read_masks[r] & ~write_masks[r]))
             | (data & write_masks[r]);
}
 
void
CPZero::mfc0_emulate(uint32 instr, uint32 pc)
{
    cpu->put_reg (CPU::rt (instr), read_reg (CPU::rd (instr)));
}
 
void
CPZero::mtc0_emulate(uint32 instr, uint32 pc)
{
    write_reg (CPU::rd (instr), cpu->get_reg (CPU::rt (instr)));
}
 
void
CPZero::bc0x_emulate(uint32 instr, uint32 pc)
{
    uint16 condition = CPU::rt (instr);
    switch (condition) {
    case 0: /* bc0f */ if (! cpCond ()) { cpu->branch (instr, pc); } break;
    case 1: /* bc0t */ if (cpCond ()) { cpu->branch (instr, pc); } break;
    case 2: /* bc0fl - not valid, but not reserved(A-17, H&K) - no-op. */ break;
    case 3: /* bc0tl - not valid, but not reserved(A-21, H&K) - no-op. */ break;
    default: cpu->exception (RI); break; /* reserved */
    }
}
 
void
CPZero::tlbr_emulate(uint32 instr, uint32 pc)
{
    reg[EntryHi] = (tlb[(reg[Index] & Index_Index_MASK) >> 8].entryHi) &
        write_masks[EntryHi];
    reg[EntryLo] = (tlb[(reg[Index] & Index_Index_MASK) >> 8].entryLo) &
        write_masks[EntryLo];
}
 
void
CPZero::tlb_write(unsigned index)
{
    tlb[index].entryHi = read_reg(EntryHi);
    tlb[index].entryLo = read_reg(EntryLo);
}
 
void
CPZero::tlbwi_emulate(uint32 instr, uint32 pc)
{
    tlb_write ((reg[Index] & Index_Index_MASK) >> 8);
}
 
void
CPZero::tlbwr_emulate(uint32 instr, uint32 pc)
{
    tlb_write ((reg[Random] & Random_Random_MASK) >> 8);
 
    adjust_random();
}
 
void
CPZero::tlbp_emulate(uint32 instr, uint32 pc)
{
    uint32 vpn = reg[EntryHi] & EntryHi_VPN_MASK;
    uint32 asid = reg[EntryHi] & EntryHi_ASID_MASK;
    int idx = find_matching_tlb_entry (vpn, asid);
    if (idx != -1)
      reg[Index] = (idx << 8);
    else
      reg[Index] = (1 << 31);
}
 
void
CPZero::rfe_emulate(uint32 instr, uint32 pc)
{
    reg[Status] = (reg[Status] & 0xfffffff0) | ((reg[Status] >> 2) & 0x0f);
}
 
void
CPZero::cpzero_emulate(uint32 instr, uint32 pc)
{
    uint16 rs = CPU::rs (instr);
    if (CPU::rs (instr) > 15) {
        switch (CPU::funct (instr)) {
        case 1: tlbr_emulate (instr, pc); break;
        case 2: tlbwi_emulate (instr, pc); break;
        case 6: tlbwr_emulate (instr, pc); break;
        case 8: tlbp_emulate (instr, pc); break;
        case 16: rfe_emulate (instr, pc); break;
        default: cpu->exception (RI, ANY, 0); break;
        }
    } else {
        switch (rs) {
        case 0: mfc0_emulate (instr, pc); break;
        case 2: cpu->exception (RI, ANY, 0); break; /* cfc0 - reserved */
        case 4: mtc0_emulate (instr, pc); break;
        case 6: cpu->exception (RI, ANY, 0); break; /* ctc0 - reserved */
        case 8: bc0x_emulate (instr,pc); break;
        default: cpu->exception (RI, ANY, 0); break;
        }
    }
}
 
void
CPZero::adjust_random(void)
{
//ao modified
    int32 r = (int32) (reg[Random] >> 8);
    if(r <= 8) r = 63; else r--;
    reg[Random] = (uint32) (r << 8);
}
 
uint32
CPZero::getIP(void)
{
    return (reg[Cause] & Cause_IP_SW_MASK) | ao_interrupts();
}
 
void
CPZero::enter_exception(uint32 pc, uint32 excCode, uint32 ce, bool dly)
{
    /* Save exception PC in EPC. */
    reg[EPC] = pc;
    /* Disable interrupts and enter Kernel mode. */
    reg[Status] = (reg[Status] & ~Status_KU_IE_MASK) |
        ((reg[Status] & Status_KU_IE_MASK) << 2);
    /* Clear Cause register BD, CE, and ExcCode fields. */
    reg[Cause] &= ~(Cause_BD_MASK|Cause_CE_MASK|Cause_ExcCode_MASK);
    /* Set Cause register CE field if this is a Coprocessor
     * Unusable exception. (If we are passed ce=-1 we don't want
     * to toggle bits in Cause.) */
    if (excCode == CpU) {
        reg[Cause] |= ((ce & 0x3) << 28);
    }
    /* Update IP, BD, ExcCode fields of Cause register. */
    reg[Cause] &= ~Cause_IP_MASK;
    reg[Cause] |= getIP () | (dly << 31) | (excCode << 2);
}
 
bool
CPZero::use_boot_excp_address(void)
{
    return (reg[Status] & Status_DS_BEV_MASK);
}
 
bool
CPZero::caches_isolated(void)
{
    return (reg[Status] & Status_DS_IsC_MASK);
}
 
bool
CPZero::caches_swapped(void)
{
    return (reg[Status] & Status_DS_SwC_MASK);
}
 
bool
CPZero::cop_usable(int coprocno)
{
    switch (coprocno) {
    case 3: return (reg[Status] & Status_CU3_MASK);
    case 2: return (reg[Status] & Status_CU2_MASK);
    case 1: return (reg[Status] & Status_CU1_MASK);
    case 0: return (reg[Status] & Status_CU0_MASK);
    default: fatal_error ("Bad coprocno passed to CPZero::cop_usable()");
    };
}
 
bool
CPZero::interrupts_enabled(void) const
{
    return (reg[Status] & Status_IEc_MASK);
}
 
bool
CPZero::interrupt_pending(void)
{
    if (! interrupts_enabled())
        return false;   /* Can't very well argue with IEc == 0... */
    /* Mask IP with the interrupt mask, and return true if nonzero: */
    return ((getIP () & (reg[Status] & Status_IM_MASK)) != 0);
}
 
//------------------------------------------------------------------------------ cpu.cc
 
/* certain fixed register numbers which are handy to know */
static const int reg_zero = 0;  /* always zero */
static const int reg_sp = 29;   /* stack pointer */
static const int reg_ra = 31;   /* return address */
 
/* pointer to CPU method returning void and taking two uint32's */
typedef void (CPU::*emulate_funptr)(uint32, uint32);
 
CPU::CPU () : last_epc (0), last_prio (0),
             cpzero (new CPZero (this)), delay_state (NORMAL)
{
    reg[reg_zero] = 0;
}
 
CPU::~CPU() {
}
 
void CPU::reset () {
    reg[reg_zero] = 0;
    pc = 0xbfc00000;
    cpzero->reset();
}
 
int
CPU::exception_priority(uint16 excCode, int mode) const
{
    /* See doc/excprio for an explanation of this table. */
    static const struct excPriority prio[] = {
        {1, AdEL, INSTFETCH},
        {2, TLBL, INSTFETCH}, {2, TLBS, INSTFETCH},
        {3, IBE, ANY},
        {4, Ov, ANY}, {4, Tr, ANY}, {4, Sys, ANY},
        {4, Bp, ANY}, {4, RI, ANY}, {4, CpU, ANY},
        {5, AdEL, DATALOAD}, {5, AdES, ANY},
        {6, TLBL, DATALOAD}, {6, TLBS, DATALOAD},
        {6, TLBL, DATASTORE}, {6, TLBS, DATASTORE},
        {7, Mod, ANY},
        {8, DBE, ANY},
        {9, Int, ANY},
        {0, ANY, ANY} /* catch-all */
    };
    const struct excPriority *p;
 
    for (p = prio; p->priority != 0; p++) {
        if (excCode == p->excCode || p->excCode == ANY) {
            if (mode == p->mode || p->mode == ANY) {
                return p->priority;
            }
        }
    }
    return 0;
}
 
void
CPU::exception(uint16 excCode, int mode /* = ANY */, int coprocno /* = -1 */)
{
printf("Exception: code: 0x%x, mode: %x, coprocno: %x\n", (uint32)excCode, mode, coprocno);
    int prio;
    uint32 base, vector, epc;
    bool delaying = (delay_state == DELAYSLOT);
 
    /* step() ensures that next_epc will always contain the correct
     * EPC whenever exception() is called.
     */
    epc = next_epc;
 
    /* Prioritize exception -- if the last exception to occur _also_ was
     * caused by this EPC, only report this exception if it has a higher
     * priority.  Otherwise, exception handling terminates here,
     * because only one exception will be reported per instruction
     * (as per MIPS RISC Architecture, p. 6-35). Note that this only
     * applies IFF the previous exception was caught during the current
     * _execution_ of the instruction at this EPC, so we check that
     * EXCEPTION_PENDING is true before aborting exception handling.
     * (This flag is reset by each call to step().)
     */
    prio = exception_priority(excCode, mode);
    if (epc == last_epc) {
        if (prio <= last_prio && exception_pending) {
            return;
        } else {
            last_prio = prio;
        }
    }
    last_epc = epc;
 
    /* Set processor to Kernel mode, disable interrupts, and save 
     * exception PC.
     */
    cpzero->enter_exception(epc,excCode,coprocno,delaying);
 
    /* Calculate the exception handler address; this is of the form BASE +
     * VECTOR. The BASE is determined by whether we're using boot-time
     * exception vectors, according to the BEV bit in the CP0 Status register.
     */
    if (cpzero->use_boot_excp_address()) {
        base = 0xbfc00100;
    } else {
        base = 0x80000000;
    }
 
    /* Do we have a User TLB Miss exception? If so, jump to the
     * User TLB Miss exception vector, otherwise jump to the
     * common exception vector.
     */
    if ((excCode == TLBL || excCode == TLBS) && (cpzero->tlb_miss_user)) {
        vector = 0x000;
    } else {
        vector = 0x080;
    }
 
    pc = base + vector;
    exception_pending = true;
}
 
/* emulation of instructions */
void
CPU::cpzero_emulate(uint32 instr, uint32 pc)
{
    cpzero->cpzero_emulate(instr, pc);
}
 
/* Called when the program wants to use coprocessor COPROCNO, and there
 * isn't any implementation for that coprocessor.
 * Results in a Coprocessor Unusable exception, along with an error
 * message being printed if the coprocessor is marked usable in the
 * CP0 Status register.
 */
void
CPU::cop_unimpl (int coprocno, uint32 instr, uint32 pc)
{
    exception (CpU, ANY, coprocno);
}
 
void
CPU::cpone_emulate(uint32 instr, uint32 pc)
{
    /* If it's a cfc1 <reg>, $0 then we copy 0 into reg,
        * which is supposed to mean there is NO cp1... 
        * for now, though, ANYTHING else asked of cp1 results
        * in the default "unimplemented" behavior. */
    if (cpzero->cop_usable (1) && rs (instr) == 2
                && rd (instr) == 0) {
        reg[rt (instr)] = 0; /* No cp1. */
    } else {
        cop_unimpl (1, instr, pc);
    }
}
 
void
CPU::cptwo_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (2, instr, pc);
}
 
void
CPU::cpthree_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (3, instr, pc);
}
 
void
CPU::control_transfer (uint32 new_pc)
{
    delay_state = DELAYING;
    delay_pc = new_pc;
}
 
/// calc_jump_target - Calculate the address to jump to as a result of
/// the J-format (jump) instruction INSTR at address PC.  (PC is the address
/// of the jump instruction, and INSTR is the jump instruction word.)
///
uint32
CPU::calc_jump_target (uint32 instr, uint32 pc)
{
    // Must use address of delay slot (pc + 4) to calculate.
    return ((pc + 4) & 0xf0000000) | (jumptarg(instr) << 2);
}
 
void
CPU::jump(uint32 instr, uint32 pc)
{
    control_transfer (calc_jump_target (instr, pc));
}
 
void
CPU::j_emulate(uint32 instr, uint32 pc)
{
    jump (instr, pc);
}
 
void
CPU::jal_emulate(uint32 instr, uint32 pc)
{
    jump (instr, pc);
    // RA gets addr of instr after delay slot (2 words after this one).
    reg[reg_ra] = pc + 8;
}
 
/// calc_branch_target - Calculate the address to jump to for the
/// PC-relative branch for which the offset is specified by the immediate field
/// of the branch instruction word INSTR, with the program counter equal to PC.
/// 
uint32
CPU::calc_branch_target(uint32 instr, uint32 pc)
{
    return (pc + 4) + (s_immed(instr) << 2);
}
 
void
CPU::branch(uint32 instr, uint32 pc)
{
    control_transfer (calc_branch_target (instr, pc));
}
 
void
CPU::beq_emulate(uint32 instr, uint32 pc)
{
    if (reg[rs(instr)] == reg[rt(instr)])
        branch (instr, pc);
}
 
void
CPU::bne_emulate(uint32 instr, uint32 pc)
{
    if (reg[rs(instr)] != reg[rt(instr)])
        branch (instr, pc);
}
 
void
CPU::blez_emulate(uint32 instr, uint32 pc)
{
    if (rt(instr) != 0) {
        exception(RI);
        return;
    }
    if (reg[rs(instr)] == 0 || (reg[rs(instr)] & 0x80000000))
        branch(instr, pc);
}
 
void
CPU::bgtz_emulate(uint32 instr, uint32 pc)
{
    if (rt(instr) != 0) {
        exception(RI);
        return;
    }
    if (reg[rs(instr)] != 0 && (reg[rs(instr)] & 0x80000000) == 0)
        branch(instr, pc);
}
 
void
CPU::addi_emulate(uint32 instr, uint32 pc)
{
    int32 a, b, sum;
 
    a = (int32)reg[rs(instr)];
    b = s_immed(instr);
    sum = a + b;
    if ((a < 0 && b < 0 && !(sum < 0)) || (a >= 0 && b >= 0 && !(sum >= 0))) {
        exception(Ov);
        return;
    } else {
        reg[rt(instr)] = (uint32)sum;
    }
}
 
void
CPU::addiu_emulate(uint32 instr, uint32 pc)
{
    int32 a, b, sum;
 
    a = (int32)reg[rs(instr)];
    b = s_immed(instr);
    sum = a + b;
    reg[rt(instr)] = (uint32)sum;
}
 
void
CPU::slti_emulate(uint32 instr, uint32 pc)
{
    int32 s_rs = reg[rs(instr)];
 
    if (s_rs < s_immed(instr)) {
        reg[rt(instr)] = 1;
    } else {
        reg[rt(instr)] = 0;
    }
}
 
void
CPU::sltiu_emulate(uint32 instr, uint32 pc)
{
    if (reg[rs(instr)] < (uint32)(int32)s_immed(instr)) {
        reg[rt(instr)] = 1;
    } else {
        reg[rt(instr)] = 0;
    }
}
 
void
CPU::andi_emulate(uint32 instr, uint32 pc)
{
    reg[rt(instr)] = (reg[rs(instr)] & 0x0ffff) & immed(instr);
}
 
void
CPU::ori_emulate(uint32 instr, uint32 pc)
{
    reg[rt(instr)] = reg[rs(instr)] | immed(instr);
}
 
void
CPU::xori_emulate(uint32 instr, uint32 pc)
{
    reg[rt(instr)] = reg[rs(instr)] ^ immed(instr);
}
 
void
CPU::lui_emulate(uint32 instr, uint32 pc)
{
    reg[rt(instr)] = immed(instr) << 16;
}
 
void
CPU::lb_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base;
    int8 byte;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch byte.
     * Because it is assigned to a signed variable (int32 byte)
     * it will be sign-extended.
     */
    byte = ao_fetch_byte(phys, cacheable, isolated);
    if (exception_pending) return;
 
    /* Load target register with data. */
    reg[rt(instr)] = byte;
}
 
void
CPU::lh_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base;
    int16 halfword;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* This virtual address must be halfword-aligned. */
    if (virt % 2 != 0) {
        exception(AdEL,DATALOAD);
        return;
    }
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch halfword.
     * Because it is assigned to a signed variable (int32 halfword)
     * it will be sign-extended.
     */
    halfword = ao_fetch_halfword(phys, cacheable, isolated);
    if (exception_pending) return;
 
    /* Load target register with data. */
    reg[rt(instr)] = halfword;
}
 
/* The lwr and lwl algorithms here are taken from SPIM 6.0,
 * since I didn't manage to come up with a better way to write them.
 * Improvements are welcome.
 */
uint32
CPU::lwr(uint32 regval, uint32 memval, uint8 offset)
{
    switch (offset)
    {
        /* The SPIM source claims that "The description of the
            * little-endian case in Kane is totally wrong." The fact
            * that I ripped off the LWR algorithm from them could be
            * viewed as a sort of passive assumption that their claim
            * is correct.
            */
        case 0: /* 3 in book */
            return memval;
        case 1: /* 0 in book */
            return (regval & 0xff000000) | ((memval & 0xffffff00) >> 8);
        case 2: /* 1 in book */
            return (regval & 0xffff0000) | ((memval & 0xffff0000) >> 16);
        case 3: /* 2 in book */
            return (regval & 0xffffff00) | ((memval & 0xff000000) >> 24);
    }
    fatal_error("Invalid offset %x passed to lwr\n", offset);
}
 
uint32
CPU::lwl(uint32 regval, uint32 memval, uint8 offset)
{
    switch (offset)
    {
        case 0: return (memval & 0xff) << 24 | (regval & 0xffffff);
        case 1: return (memval & 0xffff) << 16 | (regval & 0xffff);
        case 2: return (memval & 0xffffff) << 8 | (regval & 0xff);
        case 3: return memval;
    }
    fatal_error("Invalid offset %x passed to lwl\n", offset);
}
 
void
CPU::lwl_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, wordvirt, base, memword;
    uint8 which_byte;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
    /* We request the word containing the byte-address requested. */
    wordvirt = virt & ~0x03UL;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(wordvirt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch word. */
    memword = ao_fetch_word(phys, DATALOAD, cacheable, isolated);
    if (exception_pending) return;
 
    /* Insert bytes into the left side of the register. */
    which_byte = virt & 0x03;
    reg[rt(instr)] = lwl(reg[rt(instr)], memword, which_byte);
}
 
void
CPU::lw_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base, word;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* This virtual address must be word-aligned. */
    if (virt % 4 != 0) {
        exception(AdEL,DATALOAD);
        return;
    }
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch word. */
    word = ao_fetch_word(phys, DATALOAD, cacheable, isolated);
    if (exception_pending) return;
 
    /* Load target register with data. */
    reg[rt(instr)] = word;
}
 
void
CPU::lbu_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base, byte;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch byte.  */
    byte = ao_fetch_byte(phys, cacheable, isolated) & 0x000000ff;
    if (exception_pending) return;
 
    /* Load target register with data. */
    reg[rt(instr)] = byte;
}
 
void
CPU::lhu_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base, halfword;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* This virtual address must be halfword-aligned. */
    if (virt % 2 != 0) {
        exception(AdEL,DATALOAD);
        return;
    }
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch halfword.  */
    halfword = ao_fetch_halfword(phys, cacheable, isolated) & 0x0000ffff;
    if (exception_pending) return;
 
    /* Load target register with data. */
    reg[rt(instr)] = halfword;
}
 
void
CPU::lwr_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, wordvirt, base, memword;
    uint8 which_byte;
    int32 offset;
    bool cacheable, isolated;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
    /* We request the word containing the byte-address requested. */
    wordvirt = virt & ~0x03UL;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(wordvirt, DATALOAD, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Fetch word. */
    memword = ao_fetch_word(phys, DATALOAD, cacheable, isolated);
    if (exception_pending) return;
 
    /* Insert bytes into the left side of the register. */
    which_byte = virt & 0x03;
    reg[rt(instr)] = lwr(reg[rt(instr)], memword, which_byte);
}
 
void
CPU::sb_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base;
    uint8 data;
    int32 offset;
    bool cacheable, isolated;
 
    /* Load data from register. */
    data = reg[rt(instr)] & 0x0ff;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATASTORE, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Store byte. */
    ao_store_byte(phys, data, cacheable, isolated);
}
 
void
CPU::sh_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base;
    uint16 data;
    int32 offset;
    bool cacheable, isolated;
 
    /* Load data from register. */
    data = reg[rt(instr)] & 0x0ffff;
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* This virtual address must be halfword-aligned. */
    if (virt % 2 != 0) {
        exception(AdES,DATASTORE);
        return;
    }
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATASTORE, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Store halfword. */
    ao_store_halfword(phys, data, cacheable, isolated);
}
 
uint32
CPU::swl(uint32 regval, uint32 memval, uint8 offset)
{
    switch (offset) {
        case 0: return (memval & 0xffffff00) | (regval >> 24 & 0xff); 
        case 1: return (memval & 0xffff0000) | (regval >> 16 & 0xffff); 
        case 2: return (memval & 0xff000000) | (regval >> 8 & 0xffffff); 
        case 3: return regval; 
    }
    fatal_error("Invalid offset %x passed to swl\n", offset);
}
 
uint32
CPU::swr(uint32 regval, uint32 memval, uint8 offset)
{
    switch (offset) {
        case 0: return regval; 
        case 1: return ((regval << 8) & 0xffffff00) | (memval & 0xff); 
        case 2: return ((regval << 16) & 0xffff0000) | (memval & 0xffff); 
        case 3: return ((regval << 24) & 0xff000000) | (memval & 0xffffff); 
    }
    fatal_error("Invalid offset %x passed to swr\n", offset);
}
 
void
CPU::swl_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, wordvirt, base, regdata, memdata;
    int32 offset;
    uint8 which_byte;
    bool cacheable, isolated;
 
    /* Load data from register. */
    regdata = reg[rt(instr)];
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
    /* We request the word containing the byte-address requested. */
    wordvirt = virt & ~0x03UL;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(wordvirt, DATASTORE, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Read data from memory. */
    //memdata = ao_fetch_word(phys, DATASTORE, cacheable);
    //if (exception_pending) return;
 
    /* Write back the left side of the register. */
    which_byte = virt & 0x03UL;
    //ao_store_word(phys, swl(regdata, memdata, which_byte), cacheable);
    uint32 store_value =
        (which_byte == 0)?  (regdata >> 24 & 0xff) :
        (which_byte == 1)?  (regdata >> 16 & 0xffff) :
        (which_byte == 2)?  (regdata >> 8 & 0xffffff) :
                            regdata;
    uint32 store_byteena =
        (which_byte == 0)?  0b0001 :
        (which_byte == 1)?  0b0011 :
        (which_byte == 2)?  0b0111 :
                            0b1111;
    ao_store_word(phys, store_value, cacheable, isolated, store_byteena);
}
 
void
CPU::sw_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, base, data;
    int32 offset;
    bool cacheable, isolated;
 
    /* Load data from register. */
    data = reg[rt(instr)];
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
 
    /* This virtual address must be word-aligned. */
    if (virt % 4 != 0) {
        exception(AdES,DATASTORE);
        return;
    }
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(virt, DATASTORE, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Store word. */
    ao_store_word(phys, data, cacheable, isolated);
}
 
void
CPU::swr_emulate(uint32 instr, uint32 pc)
{
    uint32 phys, virt, wordvirt, base, regdata, memdata;
    int32 offset;
    uint8 which_byte;
    bool cacheable, isolated;
 
    /* Load data from register. */
    regdata = reg[rt(instr)];
 
    /* Calculate virtual address. */
    base = reg[rs(instr)];
    offset = s_immed(instr);
    virt = base + offset;
    /* We request the word containing the byte-address requested. */
    wordvirt = virt & ~0x03UL;
 
    /* Translate virtual address to physical address. */
    phys = cpzero->address_trans(wordvirt, DATASTORE, &cacheable, &isolated);
    if (exception_pending) return;
 
    /* Read data from memory. */
    //memdata = ao_fetch_word(phys, DATASTORE, cacheable);
    //if (exception_pending) return;
 
    /* Write back the right side of the register. */
    which_byte = virt & 0x03UL;
    //ao_store_word(phys, swr(regdata, memdata, which_byte), cacheable);
 
    uint32 store_value =
        (which_byte == 0)?  regdata :
        (which_byte == 1)?  ((regdata << 8) & 0xffffff00) :
        (which_byte == 2)?  ((regdata << 16) & 0xffff0000) :
                            ((regdata << 24) & 0xff000000);
    uint32 store_byteena =
        (which_byte == 0)?  0b1111 :
        (which_byte == 1)?  0b1110 :
        (which_byte == 2)?  0b1100 :
                            0b1000;
    ao_store_word(phys, store_value, cacheable, isolated, store_byteena);
}
 
void
CPU::lwc1_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (1, instr, pc);
}
 
void
CPU::lwc2_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (2, instr, pc);
}
 
void
CPU::lwc3_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (3, instr, pc);
}
 
void
CPU::swc1_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (1, instr, pc);
}
 
void
CPU::swc2_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (2, instr, pc);
}
 
void
CPU::swc3_emulate(uint32 instr, uint32 pc)
{
    cop_unimpl (3, instr, pc);
}
 
void
CPU::sll_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = reg[rt(instr)] << shamt(instr);
}
 
int32
srl(int32 a, int32 b)
{
    if (b == 0) {
        return a;
    } else if (b == 32) {
        return 0;
    } else {
        return (a >> b) & ((1 << (32 - b)) - 1);
    }
}
 
int32
sra(int32 a, int32 b)
{
    if (b == 0) {
        return a;
    } else {
        return (a >> b) | (((a >> 31) & 0x01) * (((1 << b) - 1) << (32 - b)));
    }
}
 
void
CPU::srl_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = srl(reg[rt(instr)], shamt(instr));
}
 
void
CPU::sra_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = sra(reg[rt(instr)], shamt(instr));
}
 
void
CPU::sllv_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = reg[rt(instr)] << (reg[rs(instr)] & 0x01f);
}
 
void
CPU::srlv_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = srl(reg[rt(instr)], reg[rs(instr)] & 0x01f);
}
 
void
CPU::srav_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = sra(reg[rt(instr)], reg[rs(instr)] & 0x01f);
}
 
void
CPU::jr_emulate(uint32 instr, uint32 pc)
{
    if (reg[rd(instr)] != 0) {
        exception(RI);
        return;
    }
    control_transfer (reg[rs(instr)]);
}
 
void
CPU::jalr_emulate(uint32 instr, uint32 pc)
{
    control_transfer (reg[rs(instr)]);
    /* RA gets addr of instr after delay slot (2 words after this one). */
    reg[rd(instr)] = pc + 8;
}
 
void
CPU::syscall_emulate(uint32 instr, uint32 pc)
{
    exception(Sys);
}
 
void
CPU::break_emulate(uint32 instr, uint32 pc)
{
    exception(Bp);
}
 
void
CPU::mfhi_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = hi;
}
 
void
CPU::mthi_emulate(uint32 instr, uint32 pc)
{
    if (rd(instr) != 0) {
        exception(RI);
        return;
    }
    hi = reg[rs(instr)];
}
 
void
CPU::mflo_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = lo;
}
 
void
CPU::mtlo_emulate(uint32 instr, uint32 pc)
{
    if (rd(instr) != 0) {
        exception(RI);
        return;
    }
    lo = reg[rs(instr)];
}
 
void
CPU::mult_emulate(uint32 instr, uint32 pc)
{
    if (rd(instr) != 0) {
        exception(RI);
        return;
    }
    mult64s(&hi, &lo, reg[rs(instr)], reg[rt(instr)]);
}
 
void
CPU::mult64(uint32 *hi, uint32 *lo, uint32 n, uint32 m)
{
    uint64 result;
    result = ((uint64)n) * ((uint64)m);
    *hi = (uint32) (result >> 32);
    *lo = (uint32) result;
}
 
void
CPU::mult64s(uint32 *hi, uint32 *lo, int32 n, int32 m)
{
    int64 result;
    result = ((int64)n) * ((int64)m);
    *hi = (uint32) (result >> 32);
    *lo = (uint32) result;
}
 
void
CPU::multu_emulate(uint32 instr, uint32 pc)
{
    if (rd(instr) != 0) {
        exception(RI);
        return;
    }
    mult64(&hi, &lo, reg[rs(instr)], reg[rt(instr)]);
}
 
void
CPU::div_emulate(uint32 instr, uint32 pc)
{
    int32 signed_rs = (int32)reg[rs(instr)];
    int32 signed_rt = (int32)reg[rt(instr)];
 
    if(signed_rt == 0) {
        lo = (signed_rs >= 0)? 0xFFFFFFFF : 0x00000001;
        hi = signed_rs;
    }
    else {
        lo = signed_rs / signed_rt;
        hi = signed_rs % signed_rt;
    }
}
 
void
CPU::divu_emulate(uint32 instr, uint32 pc)
{
    if(reg[rt(instr)] == 0) {
        lo = 0xFFFFFFFF;
        hi = reg[rs(instr)];
    }
    else {
        lo = reg[rs(instr)] / reg[rt(instr)];
        hi = reg[rs(instr)] % reg[rt(instr)];
    }
}
 
void
CPU::add_emulate(uint32 instr, uint32 pc)
{
    int32 a, b, sum;
    a = (int32)reg[rs(instr)];
    b = (int32)reg[rt(instr)];
    sum = a + b;
    if ((a < 0 && b < 0 && !(sum < 0)) || (a >= 0 && b >= 0 && !(sum >= 0))) {
        exception(Ov);
        return;
    } else {
        reg[rd(instr)] = (uint32)sum;
    }
}
 
void
CPU::addu_emulate(uint32 instr, uint32 pc)
{
    int32 a, b, sum;
    a = (int32)reg[rs(instr)];
    b = (int32)reg[rt(instr)];
    sum = a + b;
    reg[rd(instr)] = (uint32)sum;
}
 
void
CPU::sub_emulate(uint32 instr, uint32 pc)
{
    int32 a, b, diff;
    a = (int32)reg[rs(instr)];
    b = (int32)reg[rt(instr)];
    diff = a - b;
    if ((a < 0 && !(b < 0) && !(diff < 0)) || (!(a < 0) && b < 0 && diff < 0)) {
        exception(Ov);
        return;
    } else {
        reg[rd(instr)] = (uint32)diff;
    }
}
 
void
CPU::subu_emulate(uint32 instr, uint32 pc)
{
    int32 a, b, diff;
    a = (int32)reg[rs(instr)];
    b = (int32)reg[rt(instr)];
    diff = a - b;
    reg[rd(instr)] = (uint32)diff;
}
 
void
CPU::and_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = reg[rs(instr)] & reg[rt(instr)];
}
 
void
CPU::or_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = reg[rs(instr)] | reg[rt(instr)];
}
 
void
CPU::xor_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = reg[rs(instr)] ^ reg[rt(instr)];
}
 
void
CPU::nor_emulate(uint32 instr, uint32 pc)
{
    reg[rd(instr)] = ~(reg[rs(instr)] | reg[rt(instr)]);
}
 
void
CPU::slt_emulate(uint32 instr, uint32 pc)
{
    int32 s_rs = (int32)reg[rs(instr)];
    int32 s_rt = (int32)reg[rt(instr)];
    if (s_rs < s_rt) {
        reg[rd(instr)] = 1;
    } else {
        reg[rd(instr)] = 0;
    }
}
 
void
CPU::sltu_emulate(uint32 instr, uint32 pc)
{
    if (reg[rs(instr)] < reg[rt(instr)]) {
        reg[rd(instr)] = 1;
    } else {
        reg[rd(instr)] = 0;
    }
}
 
void
CPU::bltz_emulate(uint32 instr, uint32 pc)
{
    if ((int32)reg[rs(instr)] < 0)
        branch(instr, pc);
}
 
void
CPU::bgez_emulate(uint32 instr, uint32 pc)
{
    if ((int32)reg[rs(instr)] >= 0)
        branch(instr, pc);
}
 
/* As with JAL, BLTZAL and BGEZAL cause RA to get the address of the
 * instruction two words after the current one (pc + 8).
 */
void
CPU::bltzal_emulate(uint32 instr, uint32 pc)
{
    reg[reg_ra] = pc + 8;
    if ((int32)reg[rs(instr)] < 0)
        branch(instr, pc);
}
 
void
CPU::bgezal_emulate(uint32 instr, uint32 pc)
{
    reg[reg_ra] = pc + 8;
    if ((int32)reg[rs(instr)] >= 0)
        branch(instr, pc);
}
 
/* reserved instruction */
void
CPU::RI_emulate(uint32 instr, uint32 pc)
{
    exception(RI);
}
 
/* dispatching */
int
CPU::step(bool debug)
{
    // Table of emulation functions.
    static const emulate_funptr opcodeJumpTable[] = {
        &CPU::funct_emulate, &CPU::regimm_emulate,  &CPU::j_emulate,
        &CPU::jal_emulate,   &CPU::beq_emulate,     &CPU::bne_emulate,
        &CPU::blez_emulate,  &CPU::bgtz_emulate,    &CPU::addi_emulate,
        &CPU::addiu_emulate, &CPU::slti_emulate,    &CPU::sltiu_emulate,
        &CPU::andi_emulate,  &CPU::ori_emulate,     &CPU::xori_emulate,
        &CPU::lui_emulate,   &CPU::cpzero_emulate,  &CPU::cpone_emulate,
        &CPU::cptwo_emulate, &CPU::cpthree_emulate, &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::RI_emulate,      &CPU::lb_emulate,
        &CPU::lh_emulate,    &CPU::lwl_emulate,     &CPU::lw_emulate,
        &CPU::lbu_emulate,   &CPU::lhu_emulate,     &CPU::lwr_emulate,
        &CPU::RI_emulate,    &CPU::sb_emulate,      &CPU::sh_emulate,
        &CPU::swl_emulate,   &CPU::sw_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::swr_emulate,     &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::lwc1_emulate,    &CPU::lwc2_emulate,
        &CPU::lwc3_emulate,  &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,    &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::swc1_emulate,  &CPU::swc2_emulate,    &CPU::swc3_emulate,
        &CPU::RI_emulate,    &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate
    };
 
    // Clear exception_pending flag if it was set by a prior instruction.
    exception_pending = false;
 
    // Decrement Random register every clock cycle.
    //changed only after tlbwr
    //cpzero->adjust_random();
 
    // Save address of instruction responsible for exceptions which may occur.
    if (delay_state != DELAYSLOT)
        next_epc = pc;
 
    bool cacheable, isolated;
    uint32 real_pc;
 
    //AdE
    if (pc % 4 != 0) {
        exception(AdEL,INSTFETCH);
        goto out;
    }
 
    // Get physical address of next instruction.
    real_pc = cpzero->address_trans(pc,INSTFETCH,&cacheable,&isolated);
    if (exception_pending) {
        goto out;
    }
 
    // Fetch next instruction.
    instr = ao_fetch_word(real_pc,INSTFETCH,cacheable,isolated);
    if (exception_pending) {
        goto out;
    }
 
    //interrupt check moved below
 
    // Emulate the instruction by jumping to the appropriate emulation method.
 
static uint32 instr_cnt = 0;
if(debug) {
    printf("[%d] table: %d instr: %08x pc: %08x\n", instr_cnt, opcode(instr), instr, pc);
    for(int i=1; i<32; i++) printf("%08x ", reg[i]); printf("\n");
}
instr_cnt++;
 
    (this->*opcodeJumpTable[opcode(instr)])(instr, pc);
 
out:
    // Force register zero to contain zero.
    reg[reg_zero] = 0;
 
    // If an exception is pending, then the PC has already been changed to
    // contain the exception vector.  Return now, so that we don't clobber it.
    if (exception_pending) {
        // Instruction at beginning of exception handler is NOT in delay slot,
        // no matter what the last instruction was.
        delay_state = NORMAL;
        return 1;
    }
 
    // Recall the delay_state values: 0=NORMAL, 1=DELAYING, 2=DELAYSLOT.
    // This is what the delay_state values mean (at this point in the code):
    // DELAYING: The last instruction caused a branch to be taken.
    //  The next instruction is in the delay slot.
    //  The next instruction EPC will be PC - 4.
    // DELAYSLOT: The last instruction was executed in a delay slot.
    //  The next instruction is on the other end of the branch.
    //  The next instruction EPC will be PC.
    // NORMAL: No branch was executed; next instruction is at PC + 4.
    //  Next instruction EPC is PC.
 
    // Update the pc and delay_state values.
    pc += 4;
    was_delayed_transfer = false;
    if (delay_state == DELAYSLOT) {
        was_delayed_transfer = true;
        was_delayed_pc = pc;
 
        pc = delay_pc;
    }
    delay_state = (delay_state << 1) & 0x03; // 0->0, 1->2, 2->0
 
    // Check for a (hardware or software) interrupt.
    if (cpzero->interrupt_pending()) {
        if(delay_state != DELAYSLOT) next_epc = pc;
 
        exception(Int);
        delay_state = NORMAL;
        return 2;
    }
 
    return 0;
}
 
void
CPU::funct_emulate(uint32 instr, uint32 pc)
{
    static const emulate_funptr functJumpTable[] = {
        &CPU::sll_emulate,     &CPU::RI_emulate,
        &CPU::srl_emulate,     &CPU::sra_emulate,
        &CPU::sllv_emulate,    &CPU::RI_emulate,
        &CPU::srlv_emulate,    &CPU::srav_emulate,
        &CPU::jr_emulate,      &CPU::jalr_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::syscall_emulate, &CPU::break_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::mfhi_emulate,    &CPU::mthi_emulate,
        &CPU::mflo_emulate,    &CPU::mtlo_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::mult_emulate,    &CPU::multu_emulate,
        &CPU::div_emulate,     &CPU::divu_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::add_emulate,     &CPU::addu_emulate,
        &CPU::sub_emulate,     &CPU::subu_emulate,
        &CPU::and_emulate,     &CPU::or_emulate,
        &CPU::xor_emulate,     &CPU::nor_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::slt_emulate,     &CPU::sltu_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate,
        &CPU::RI_emulate,      &CPU::RI_emulate
    };
    (this->*functJumpTable[funct(instr)])(instr, pc);
}
 
void
CPU::regimm_emulate(uint32 instr, uint32 pc)
{
    switch(rt(instr))
    {
        case 0: bltz_emulate(instr, pc); break;
        case 1: bgez_emulate(instr, pc); break;
        case 16: bltzal_emulate(instr, pc); break;
        case 17: bgezal_emulate(instr, pc); break;
        default: exception(RI); break; /* reserved instruction */
    }
}
 
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
 

Compare with Previous | Blame | View Log

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.