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

Subversion Repositories ion

[/] [ion/] [trunk/] [src/] [common/] [opcode_emu.s] - Rev 191

Go to most recent revision | Compare with Previous | Blame | View Log

#-------------------------------------------------------------------------------
# opcode_emu.s -- Emulation of some mips-32 opcodes in trap handler
#
# Operation:
# 1.- If the trapped opcode is one of the emulated opcodes, it is emulated.
# 2.- Otherwise, nothing is done (as if the trapped opcode was a NOP) and a flag
#     is set in the emu_frame area (meant for debugging, mostly).
# 3.- In either case, if the trapped opcode is in a jump delay slot, the jump
#     instruction is emulated. 
#     FIXME it isn't yet
#
# Uses a small workspace in the BSS section. Does NOT use the regular stack
# nor makes any assumptions about any registers, including the sp.
#
#-------------------------------------------------------------------------------
# This is a trap handler meant to emulate those few MIPS32r1/r2 opcodes that
# gcc commonly emits even for a -mips1 target. This happens specially when using
# soft floating point.
# The opcodes chosen for emulation are those actually observed in real gcc 
# programs. This is easier, for the time being, than replacing a whole host of
# gcc support functions.
#
# Emulates the following opcodes:
#   EXT, INS, CLO, CLZ
#
# FIXME these opcodes will be emulated eventually:
#   MUL (3-reg version), LWL, LWR, SWL, SWR
#-------------------------------------------------------------------------------
 
    # Size of work space
    .set EMU_FRAME_SIZE, 64
 
    # Reserve work space in BSS segment
    .comm emu_frame, EMU_FRAME_SIZE
 
    .text
    .align  2
    .set    noreorder
 
    .global opcode_emu
    # $k0 = cp0.cause on entry, other registers have their original value
opcode_emu:
    # Save the CPU state.
    # No nested exceptions are to be allowed, so we only save the registers 
    # we're going to use and leave the CP0 registers unsaved.
    la      $k1,emu_frame+EMU_FRAME_SIZE-4
    sw      $sp,-16($k1)
    sw      $ra,-20($k1)
    sw      $t0,-24($k1)
    sw      $t1,-28($k1)
    sw      $t2,-32($k1)
    move    $sp,$k1
 
    mfc0    $t0,$14             # get bad opcode (or branch opcode if in DS)
    lw      $k0,0($t0)
 
    # Handle delay slot situation: emulate jump if necessary
    mfc0    $k1,$13             # Check bit 31 (BD) from C0 cause register
    bltzal  $k1,emulate_branch
    nop
 
    # decode instruction: either SPECIAL3 or SPECIAL2
    srl     $t1,$k0,26
    xori    $t0,$t1,0x01f
    beqz    $t0,mips32_special3
    xori    $t0,$t1,0x01c
    beqz    $t0,mips32_special2
    nop
    # if it was none of the above we will just ignore it
    # Write ignored opcode to some debug register for external reference
    # (this is to be used bu the SW simulator, mostly)
    li      $t0,0x20010000
    sw      $k0,1024($t0)
    # Run into opcode_emu_return
 
opcode_emu_return:
    # restore modified registers
    lw      $t2,-32($sp)
    lw      $t1,-28($sp)
    lw      $t0,-24($sp)
    lw      $ra,-20($sp)
    lw      $sp,-16($sp)
    # return to interrupted code, handling trap-in-delay-slot cases properly
    mfc0    $k1,$14             # C0_EPC=14 (Exception PC)
    mfc0    $k0,$13             # Get bit 31 (BD) from C0 cause register
    srl     $k0,31
    andi    $k0,$k0,1
    bnez    $k0,mips32_emu_return_delay_slot
    addi    $k1,$k1,4           # skip 'victim' instruction
    jr      $k1
    nop
mips32_emu_return_delay_slot:
    addi    $k1,$k1,4           # skip jump instruction too (it's been emulated)
    jr      $k1                 # (we just added 8 to epc)
    rfe
 
    # SPECIAL2 opcodes
    # entry: k0=opcode
mips32_special2:
    andi    $k1,$k0,0x03f
    xori    $t0,$k1,0x020
    beqz    $t0,mips32_CLZ
    xori    $t0,$k1,0x021
    beqz    $t0,mips32_CLO
    nop
    # all other special-3 opcodes go unemulated
    li      $t0,0x20010000      # Write ignored opcode to debug register
    sw      $k0,1024($t0)
    j       opcode_emu_return
    nop
 
    # SPECIAL3 opcodes
    # $k0 = opcode
mips32_special3:
    andi    $k1,$k0,0x03f
    xori    $t0,$k1,0x0
    beqz    $t0,mips32_EXT
    xori    $t0,$k1,0x04
    beqz    $t0,mips32_INS
    nop
    # all other special-3 opcodes go unemulated
    li      $t0,0x20010000      # Write ignored opcode to debug register
    sw      $k0,1024($t0)    
    j       opcode_emu_return
    nop
 
    # Get the branch opcode, decode it and emulate it
    # entry: $k0 = opcode, $t0 = address of branch
    # exit: $k0 = opcode that triggered exception (in branch delay)
emulate_branch:
 
    # FIXME branch emulation is missing!
    lw      $k0,4($t0)          # read actual guilty opcode
    jr      $ra
    nop
 
    #---- Branch emulation routines --------------------------------------------
    # FIXME branch emulation missing
 
    #---- Opcode emulation routines --------------------------------------------
 
    # CLZ: rd <- count leading zeros on rs
mips32_CLZ:
    jal     get_source_register             # $k1 = source register (Rs)
    lui     $t2,0x8000                      # $t2 = bit mask
 
    move    $t0,$zero                       # $t0 = counter
mips32_clz_loop:
    and     $t1,$k1,$t2
    bnez    $t1,mips32_clz_done
    srl     $t2,$t2,1
    bnez    $t2,mips32_clz_loop
    addiu   $t0,$t0,1
mips32_clz_done:
    j       mips32_save_result              # put result in dest reg and return
    move    $k1,$t0
 
    # CLO: rd <- count leading ones on rs
mips32_CLO:
    jal     get_source_register             # $k1 = source register (Rs)
    lui     $t2,0x8000                      # $t2 = bit mask
 
    move    $t0,$zero                       # $t0 = counter
mips32_clo_loop:
    and     $t1,$k1,$t2
    beqz    $t1,mips32_clo_done
    srl     $t2,$t2,1
    bnez    $t2,mips32_clo_loop
    addiu   $t0,$t0,1
mips32_clo_done:
    j       mips32_save_result              # put result in dest reg and return
    move    $k1,$t0
 
    # EXT:
mips32_EXT:
    jal     get_source_register             # $k1 = source register (Rs)
    nop
    srl     $t0,$k0,6                       # $t0 = pos
    andi    $t0,$t0,0x1f
    srl     $t1,$k0,11
    andi    $t1,$t1,0x1f                    # $t1 = size-1
 
    addu    $t2,$t0,$t1                     # ...by shifting left and then right
    subu    $t2,$zero,$t2
    addiu   $t2,$t2,31
    sllv    $k1,$k1,$t2
    srlv    $k1,$k1,$t2
    j       mips32_save_result              # put result in dest reg and return
    srlv    $k1,$k1,$t0                     # k1 = bit field, zero extended
 
    # INS:
mips32_INS:
    jal     get_source_register             # $k1 = source register (Rs)
    nop
 
    srl     $t0,$k0,6                       # $t0 = pos
    andi    $t0,$t0,0x1f
    srl     $t1,$k0,11
    andi    $t1,$t1,0x1f                    # $t1 = pos+size-1
    subu    $t1,$t1,$t0                     # $t1 = size-1
 
    subu    $t3,$zero,$t1                   # $t1 = source bit field, shifted 
    addiu   $t3,$t3,31
    sllv    $t1,$k1,$t0
 
    lui     $t2,0xffff                      # $t2 = target mask (0 for bitfield)
    ori     $t2,$t2,0xffff
    sllv    $t2,$t2,$t3 
    srlv    $t2,$t2,$t3 
    sllv    $t2,$t2,$t0
    and     $t1,$t1,$t2                     # $t1 = source bit field, masked
    not     $t2,$t2
 
    # ok, we're done with the source register, and we have the following:
    # $t0=pos, $t1=size-1, $t3=left shift amount, $t4=bitfield, $t2=mask
 
    # (uses $k1 and $t0)
    jal     get_source_register             # $k1 = target register
    sll     $k0,$k0,5
    srl     $k0,$k0,5                       # restore field Rt of opcode
 
    and     $k1,$k1,$t2                     # insert bit field in Rt...
    or      $k1,$k1,$t1
 
    j       mips32_save_result              # ...and put result back in register
    nop
 
 
    #---- Common utility routines ----------------------------------------------
 
    # entry: $k0 = opcode, $k1 = result
mips32_save_result:
    srl     $t1,$k0,16
    andi    $t1,$t1,0x1f
    la      $t0,set_target_table
    sll     $t1,$t1,3
    add     $t0,$t1
    jr      $t0
    nop
set_target_done:
    j       opcode_emu_return
    nop
 
set_target_table:
    j       set_target_done
    ori     $0,$k1,0
    j       set_target_done
    ori     $1,$k1,0
    j       set_target_done
    ori     $2,$k1,0
    j       set_target_done
    ori     $3,$k1,0
    j       set_target_done
    ori     $4,$k1,0
    j       set_target_done
    ori     $5,$k1,0
    j       set_target_done
    ori     $6,$k1,0
    j       set_target_done
    ori     $7,$k1,0
    j       set_target_done
    sw      $k1,-24($sp)
    j       set_target_done
    sw      $k1,-28($sp)
    j       set_target_done
    sw      $k1,-32($sp)
    j       set_target_done
    ori     $11,$k1,0
    j       set_target_done
    ori     $12,$k1,0
    j       set_target_done
    ori     $13,$k1,0
    j       set_target_done
    ori     $14,$k1,0
    j       set_target_done
    ori     $15,$k1,0
    j       set_target_done
    ori     $16,$k1,0
    j       set_target_done
    ori     $17,$k1,0
    j       set_target_done
    ori     $18,$k1,0
    j       set_target_done
    ori     $19,$k1,0
    j       set_target_done
    ori     $20,$k1,0
    j       set_target_done
    ori     $21,$k1,0
    j       set_target_done
    ori     $22,$k1,0
    j       set_target_done
    ori     $23,$k1,0
    j       set_target_done
    ori     $24,$k1,0
    j       set_target_done
    ori     $25,$k1,0
    j       set_target_done
    ori     $26,$k1,0
    j       set_target_done
    ori     $27,$k1,0
    j       set_target_done
    ori     $28,$k1,0
    j       set_target_done
    sw      $k1,-20($sp)
    j       set_target_done
    ori     $30,$k1,0
    j       set_target_done
    sw      $k1,-16($sp)
 
    # get value of register rs (field 25..21)
    # entry: $k0=opcode
    # exit: $k1=source
get_source_register:
    sw      $ra,0($sp)
    srl     $k1,$k0,21
    andi    $k1,$k1,0x1f
    la      $t0,get_source_table
    sll     $k1,$k1,3
    add     $t0,$t0,$k1
    jalr    $t0
    nop
    lw      $ra,0($sp)
    jr      $ra
    nop
 
    # exit: $k1=source reg
get_source_table:
    jr      $ra
    ori     $k1,$0,0
    jr      $ra
    ori     $k1,$1,0
    jr      $ra
    ori     $k1,$2,0
    jr      $ra
    ori     $k1,$3,0
    jr      $ra
    ori     $k1,$4,0
    jr      $ra
    ori     $k1,$5,0
    jr      $ra
    ori     $k1,$6,0
    jr      $ra
    ori     $k1,$7,0
    jr      $ra
    lw      $k1,-24($sp)
    jr      $ra
    lw      $k1,-28($sp)
    jr      $ra
    lw      $k1,-32($sp)
    jr      $ra
    ori     $k1,$11,0
    jr      $ra
    ori     $k1,$12,0
    jr      $ra
    ori     $k1,$13,0
    jr      $ra
    ori     $k1,$14,0
    jr      $ra
    ori     $k1,$15,0
    jr      $ra
    ori     $k1,$16,0
    jr      $ra
    ori     $k1,$17,0
    jr      $ra
    ori     $k1,$18,0
    jr      $ra
    ori     $k1,$19,0
    jr      $ra
    ori     $k1,$20,0
    jr      $ra
    ori     $k1,$21,0
    jr      $ra
    ori     $k1,$22,0
    jr      $ra
    ori     $k1,$23,0
    jr      $ra
    ori     $k1,$24,0
    jr      $ra
    ori     $k1,$25,0
    jr      $ra
    ori     $k1,$26,0
    jr      $ra
    ori     $k1,$27,0
    jr      $ra
    ori     $k0,$28,0
    jr      $ra
    lw      $k1,-16($sp)
    jr      $ra
    ori     $k1,$30,0
    jr      $ra
    lw      $k1,-20($sp)
 
 

Go to most recent revision | 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.