mirror of https://github.com/xemu-project/xemu.git
1430 lines
52 KiB
C
1430 lines
52 KiB
C
/*
|
|
* DSP56300 emulator
|
|
*
|
|
* Copyright (c) 2015 espes
|
|
*
|
|
* Adapted from Hatari DSP M56001 emulation
|
|
* (C) 2003-2008 ARAnyM developer team
|
|
* Adaption to Hatari (C) 2008 by Thomas Huth
|
|
*
|
|
* 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 <stdbool.h>
|
|
#include <string.h>
|
|
#include <assert.h>
|
|
|
|
#include "qemu/osdep.h"
|
|
#include "qemu/bswap.h"
|
|
#include "dsp_cpu.h"
|
|
|
|
#define TRACE_DSP_DISASM 0
|
|
#define TRACE_DSP_DISASM_REG 0
|
|
#define TRACE_DSP_DISASM_MEM 0
|
|
|
|
#define DPRINTF(s, ...) printf(s, ## __VA_ARGS__)
|
|
|
|
#define BITMASK(x) ((1<<(x))-1)
|
|
#define ARRAYSIZE(x) (sizeof(x)/sizeof(x[0]))
|
|
|
|
// #define DSP_COUNT_IPS /* Count instruction per seconds */
|
|
|
|
|
|
/**********************************
|
|
* Defines
|
|
**********************************/
|
|
|
|
#define SIGN_PLUS 0
|
|
#define SIGN_MINUS 1
|
|
|
|
/**********************************
|
|
* Functions
|
|
**********************************/
|
|
|
|
static void dsp_postexecute_update_pc(dsp_core_t* dsp);
|
|
static void dsp_postexecute_interrupts(dsp_core_t* dsp);
|
|
|
|
static uint32_t read_memory_p(dsp_core_t* dsp, uint32_t address);
|
|
static uint32_t read_memory_disasm(dsp_core_t* dsp, int space, uint32_t address);
|
|
|
|
static void write_memory_raw(dsp_core_t* dsp, int space, uint32_t address, uint32_t value);
|
|
static void write_memory_disasm(dsp_core_t* dsp, int space, uint32_t address, uint32_t value);
|
|
|
|
static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value);
|
|
|
|
static void dsp_stack_push(dsp_core_t* dsp, uint32_t curpc, uint32_t cursr, uint16_t sshOnly);
|
|
static void dsp_stack_pop(dsp_core_t* dsp, uint32_t *curpc, uint32_t *cursr);
|
|
static void dsp_compute_ssh_ssl(dsp_core_t* dsp);
|
|
|
|
/* 56bits arithmetic */
|
|
static uint16_t dsp_abs56(uint32_t *dest);
|
|
static uint16_t dsp_asl56(uint32_t *dest, int n);
|
|
static uint16_t dsp_asr56(uint32_t *dest, int n);
|
|
static uint16_t dsp_add56(uint32_t *source, uint32_t *dest);
|
|
static uint16_t dsp_sub56(uint32_t *source, uint32_t *dest);
|
|
static void dsp_mul56(uint32_t source1, uint32_t source2, uint32_t *dest, uint8_t signe);
|
|
static void dsp_rnd56(dsp_core_t* dsp, uint32_t *dest);
|
|
static uint32_t dsp_signextend(int bits, uint32_t v);
|
|
|
|
static const dsp_interrupt_t dsp_interrupt[12] = {
|
|
{DSP_INTER_RESET , 0x00, 0, "Reset"},
|
|
{DSP_INTER_ILLEGAL , 0x3e, 0, "Illegal"},
|
|
{DSP_INTER_STACK_ERROR , 0x02, 0, "Stack Error"},
|
|
{DSP_INTER_TRACE , 0x04, 0, "Trace"},
|
|
{DSP_INTER_SWI , 0x06, 0, "Swi"},
|
|
{DSP_INTER_HOST_COMMAND , 0xff, 1, "Host Command"},
|
|
{DSP_INTER_HOST_RCV_DATA, 0x20, 1, "Host receive"},
|
|
{DSP_INTER_HOST_TRX_DATA, 0x22, 1, "Host transmit"},
|
|
{DSP_INTER_SSI_RCV_DATA_E, 0x0e, 2, "SSI receive with exception"},
|
|
{DSP_INTER_SSI_RCV_DATA , 0x0c, 2, "SSI receive"},
|
|
{DSP_INTER_SSI_TRX_DATA_E, 0x12, 2, "SSI transmit with exception"},
|
|
{DSP_INTER_SSI_TRX_DATA , 0x10, 2, "SSI tramsmit"}
|
|
};
|
|
|
|
static const int registers_tcc[16][2] = {
|
|
{DSP_REG_B,DSP_REG_A},
|
|
{DSP_REG_A,DSP_REG_B},
|
|
{DSP_REG_NULL,DSP_REG_NULL},
|
|
{DSP_REG_NULL,DSP_REG_NULL},
|
|
|
|
{DSP_REG_NULL,DSP_REG_NULL},
|
|
{DSP_REG_NULL,DSP_REG_NULL},
|
|
{DSP_REG_NULL,DSP_REG_NULL},
|
|
{DSP_REG_NULL,DSP_REG_NULL},
|
|
|
|
{DSP_REG_X0,DSP_REG_A},
|
|
{DSP_REG_X0,DSP_REG_B},
|
|
{DSP_REG_Y0,DSP_REG_A},
|
|
{DSP_REG_Y0,DSP_REG_B},
|
|
|
|
{DSP_REG_X1,DSP_REG_A},
|
|
{DSP_REG_X1,DSP_REG_B},
|
|
{DSP_REG_Y1,DSP_REG_A},
|
|
{DSP_REG_Y1,DSP_REG_B}
|
|
};
|
|
|
|
static const int registers_mask[64] = {
|
|
0, 0, 0, 0,
|
|
24, 24, 24, 24,
|
|
24, 24, 8, 8,
|
|
24, 24, 24, 24,
|
|
|
|
16, 16, 16, 16,
|
|
16, 16, 16, 16,
|
|
16, 16, 16, 16,
|
|
16, 16, 16, 16,
|
|
|
|
16, 16, 16, 16,
|
|
16, 16, 16, 16,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 16, 8, 6,
|
|
16, 16, 16, 16
|
|
};
|
|
|
|
#include "dsp_emu.inl"
|
|
|
|
#include "dsp_dis.inl"
|
|
|
|
typedef bool (*match_func_t)(uint32_t op);
|
|
|
|
typedef struct OpcodeEntry {
|
|
const char* template;
|
|
const char* name;
|
|
dis_func_t dis_func;
|
|
emu_func_t emu_func;
|
|
match_func_t match_func;
|
|
} OpcodeEntry;
|
|
|
|
static bool match_MMMRRR(uint32_t op)
|
|
{
|
|
uint32_t RRR = (op >> 8) & BITMASK(3);
|
|
uint32_t MMM = (op >> 11) & BITMASK(3);
|
|
if (MMM == 0x6) {
|
|
return RRR == 0x0 || RRR == 0x4;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
static const OpcodeEntry nonparallel_opcodes[] = {
|
|
{ "0000000101iiiiii1000d000", "add #xx, D", dis_add_imm, emu_add_imm },
|
|
{ "00000001010000001100d000", "add #xxxx, D", dis_add_long, emu_add_long },
|
|
{ "0000000101iiiiii1000d110", "and #xx, D", dis_and_imm, emu_and_imm },
|
|
{ "00000001010000001100d110", "and #xxxx, D", dis_and_long, emu_and_long },
|
|
{ "00000000iiiiiiii101110EE", "andi #xx, D", dis_andi, emu_andi },
|
|
{ "0000110000011101SiiiiiiD", "asl #ii, S2, D", dis_asl_imm, emu_asl_imm },
|
|
{ "0000110000011110010SsssD", "asl S1, S2, D", NULL, NULL },
|
|
{ "0000110000011100SiiiiiiD", "asr #ii, S2, D", dis_asr_imm, emu_asr_imm },
|
|
{ "0000110000011110011SsssD", "asr S1, S2, D", NULL, NULL },
|
|
{ "00001101000100000100CCCC", "bcc xxxx", dis_bcc_long, emu_bcc_long }, //??
|
|
{ "00000101CCCC01aaaa0aaaaa", "bcc xxx", dis_bcc_imm, emu_bcc_imm },
|
|
{ "0000110100011RRR0100CCCC", "bcc Rn", NULL, NULL },
|
|
{ "0000101101MMMRRR0S00bbbb", "bchg #n, [X or Y]:ea", dis_bchg_ea, emu_bchg_ea, match_MMMRRR },
|
|
{ "0000101100aaaaaa0S00bbbb", "bchg #n, [X or Y]:aa", dis_bchg_aa, emu_bchg_aa },
|
|
{ "0000101110pppppp0S00bbbb", "bchg #n, [X or Y]:pp", dis_bchg_pp, emu_bchg_pp },
|
|
{ "0000000101qqqqqq0S0bbbbb", "bchg #n, [X or Y]:qq", NULL, NULL },
|
|
{ "0000101111DDDDDD010bbbbb", "bchg, #n, D", dis_bchg_reg, emu_bchg_reg },
|
|
{ "0000101001MMMRRR0S00bbbb", "bclr #n, [X or Y]:ea", dis_bclr_ea, emu_bclr_ea, match_MMMRRR },
|
|
{ "0000101000aaaaaa0S00bbbb", "bclr #n, [X or Y]:aa", dis_bclr_aa, emu_bclr_aa },
|
|
{ "0000101010pppppp0S00bbbb", "bclr #n, [X or Y]:pp", dis_bclr_pp, emu_bclr_pp },
|
|
{ "0000000100qqqqqq0S00bbbb", "bclr #n, [X or Y]:qq", NULL, NULL },
|
|
{ "0000101011DDDDDD010bbbbb", "bclr #n, D", dis_bclr_reg, emu_bclr_reg },
|
|
{ "000011010001000011000000", "bra xxxx", dis_bra_long, emu_bra_long },
|
|
{ "00000101000011aaaa0aaaaa", "bra xxx", dis_bra_imm, emu_bra_imm },
|
|
{ "0000110100011RRR11000000", "bra Rn", NULL, NULL },
|
|
{ "0000110010MMMRRR0S0bbbbb", "brclr #n, [X or Y]:ea, xxxx", NULL, NULL, match_MMMRRR },
|
|
{ "0000110010aaaaaa1S0bbbbb", "brclr #n, [X or Y]:aa, xxxx", NULL, NULL },
|
|
{ "0000110011pppppp0S0bbbbb", "brclr #n, [X or Y]:pp, xxxx", dis_brclr_pp, emu_brclr_pp },
|
|
{ "0000010010qqqqqq0S0bbbbb", "brclr #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000110011DDDDDD100bbbbb", "brclr #n, S, xxxx", dis_brclr_reg, emu_brclr_reg },
|
|
{ "00000000000000100001CCCC", "brkcc", NULL, NULL },
|
|
{ "0000110010MMMRRR0S1bbbbb", "brset #n, [X or Y]:ea, xxxx", NULL, NULL, match_MMMRRR },
|
|
{ "0000110010aaaaaa1S1bbbbb", "brset #n, [X or Y]:aa, xxxx", NULL, NULL },
|
|
{ "0000110011pppppp0S1bbbbb", "brset #n, [X or Y]:pp, xxxx", dis_brset_pp, emu_brset_pp },
|
|
{ "0000010010qqqqqq0S1bbbbb", "brset #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000110011DDDDDD101bbbbb", "brset #n, S, xxxx", dis_brset_reg, emu_brset_reg },
|
|
{ "00001101000100000000CCCC", "bscc xxxx", NULL, NULL },
|
|
{ "00000101CCCC00aaaa0aaaaa", "bscc xxx", NULL, NULL },
|
|
{ "0000110100011RRR0000CCCC", "bscc Rn", NULL, NULL },
|
|
{ "0000110110MMMRRR0S0bbbbb", "bsclr #n, [X or Y]:ea, xxxx", NULL, NULL, match_MMMRRR },
|
|
{ "0000110110aaaaaa1S0bbbbb", "bsclr #n, [X or Y]:aa, xxxx", NULL, NULL },
|
|
{ "0000010010qqqqqq1S0bbbbb", "bsclr #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000110111pppppp0S0bbbbb", "bsclr #n, [X or Y]:pp, xxxx", NULL, NULL },
|
|
{ "0000110111DDDDDD100bbbbb", "bsclr, #n, S, xxxx", NULL, NULL },
|
|
{ "0000101001MMMRRR0S1bbbbb", "bset #n, [X or Y]:ea", dis_bset_ea, emu_bset_ea, match_MMMRRR },
|
|
{ "0000101000aaaaaa0S1bbbbb", "bset #n, [X or Y]:aa", dis_bset_aa, emu_bset_aa },
|
|
{ "0000101010pppppp0S1bbbbb", "bset #n, [X or Y]:pp", dis_bset_pp, emu_bset_pp },
|
|
{ "0000000100qqqqqq0S1bbbbb", "bset #n, [X or Y]:qq", NULL, NULL },
|
|
{ "0000101011DDDDDD011bbbbb", "bset, #n, D", dis_bset_reg, emu_bset_reg },
|
|
{ "000011010001000010000000", "bsr xxxx", dis_bsr_long, emu_bsr_long },
|
|
{ "00000101000010aaaa0aaaaa", "bsr xxx", dis_bsr_imm, emu_bsr_imm },
|
|
{ "0000110100011RRR10000000", "bsr Rn", NULL, NULL },
|
|
{ "0000110110MMMRRR0S1bbbbb", "bsset #n, [X or Y]:ea, xxxx", NULL, NULL, match_MMMRRR },
|
|
{ "0000110110aaaaaa1S1bbbbb", "bsset #n, [X or Y]:aa, xxxx", NULL, NULL },
|
|
{ "0000110111pppppp0S1bbbbb", "bsset #n, [X or Y]:pp, xxxx", NULL, NULL },
|
|
{ "0000010010qqqqqq1S1bbbbb", "bsset #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000110111DDDDDD101bbbbb", "bsset #n, S, xxxx", NULL, NULL },
|
|
{ "0000101101MMMRRR0S10bbbb", "btst #n, [X or Y]:ea", dis_btst_ea, emu_btst_ea, match_MMMRRR },
|
|
{ "0000101100aaaaaa0S10bbbb", "btst #n, [X or Y]:aa", dis_btst_aa, emu_btst_aa },
|
|
{ "0000101110pppppp0S10bbbb", "btst #n, [X or Y]:pp", dis_btst_pp, emu_btst_pp },
|
|
{ "0000000101qqqqqq0S10bbbb", "btst #n, [X or Y]:qq", NULL, NULL },
|
|
{ "0000101111DDDDDD0110bbbb", "btst #n, D", dis_btst_reg, emu_btst_reg },
|
|
{ "0000110000011110000000SD", "clb S, D", NULL, NULL },
|
|
{ "0000000101iiiiii1000d101", "cmp #xx, S2", dis_cmp_imm, emu_cmp_imm },
|
|
{ "00000001010000001100d101", "cmp #xxxx, S2", dis_cmp_long, emu_cmp_long },
|
|
{ "00001100000111111111gggd", "cmpu S1, S2", dis_cmpu, emu_cmpu },
|
|
{ "000000000000001000000000", "debug", NULL, NULL },
|
|
{ "00000000000000110000CCCC", "debugcc", NULL, NULL },
|
|
{ "00000000000000000000101d", "dec D", NULL /*dis_dec*/, emu_dec },
|
|
{ "000000011000000001JJd000", "div S, D", dis_div, emu_div },
|
|
{ "000000010010010s1sdkQQQQ", "dmac S1, S2, D", NULL, NULL },
|
|
{ "0000011001MMMRRR0S000000", "do [X or Y]:ea, expr", dis_do_ea, emu_do_ea, match_MMMRRR },
|
|
{ "0000011000aaaaaa0S000000", "do [X or Y]:aa, expr", dis_do_aa, emu_do_aa },
|
|
{ "00000110iiiiiiii1000hhhh", "do #xxx, expr", dis_do_imm, emu_do_imm },
|
|
{ "0000011011DDDDDD00000000", "do S, expr", dis_do_reg, emu_do_reg },
|
|
{ "000000000000001000000011", "do_f", NULL, NULL },
|
|
{ "0000011001MMMRRR0S010000", "dor [X or Y]:ea, label", NULL, NULL, match_MMMRRR },
|
|
{ "0000011000aaaaaa0S010000", "dor [X or Y]:aa, label", NULL, NULL },
|
|
{ "00000110iiiiiiii1001hhhh", "dor #xxx, label", dis_dor_imm, emu_dor_imm },
|
|
{ "0000011011DDDDDD00010000", "dor S, label", dis_dor_reg, emu_dor_reg },
|
|
{ "000000000000001000000010", "dor_f", NULL, NULL },
|
|
{ "000000000000000010001100", "enddo", NULL, emu_enddo },
|
|
{ "0000000101iiiiii1000d011", "eor #xx, D", NULL, NULL },
|
|
{ "00000001010000001100d011", "eor #xxxx, D", NULL, NULL },
|
|
{ "0000110000011010000sSSSD", "extract S1, S2, D", NULL, NULL },
|
|
{ "0000110000011000000s000D", "extract #CO, S2, D", NULL, NULL },
|
|
{ "0000110000011010100sSSSD", "extractu S1, S2, D", NULL, NULL },
|
|
{ "0000110000011000100s000D", "extractu #CO, S2, D", NULL, NULL },
|
|
{ "000000000000000000000101", "ill", NULL, emu_illegal },
|
|
{ "00000000000000000000100d", "inc D", NULL, emu_inc },
|
|
{ "00001100000110110qqqSSSD", "insert S1, S2, D", NULL, NULL },
|
|
{ "00001100000110010qqq000D", "insert #CO, S2, D", NULL, NULL },
|
|
{ "00001110CCCCaaaaaaaaaaaa", "jcc xxx", dis_jcc_imm, emu_jcc_imm },
|
|
{ "0000101011MMMRRR1010CCCC", "jcc ea", dis_jcc_ea, emu_jcc_ea, match_MMMRRR },
|
|
{ "0000101001MMMRRR1S00bbbb", "jclr #n, [X or Y]:ea, xxxx", dis_jclr_ea, emu_jclr_ea, match_MMMRRR },
|
|
{ "0000101000aaaaaa1S00bbbb", "jclr #n, [X or Y]:aa, xxxx", dis_jclr_aa, emu_jclr_aa },
|
|
{ "0000101010pppppp1S00bbbb", "jclr #n, [X or Y]:pp, xxxx", dis_jclr_pp, emu_jclr_pp },
|
|
{ "0000000110qqqqqq1S00bbbb", "jclr #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000101011DDDDDD0000bbbb", "jclr #n, S, xxxx", dis_jclr_reg, emu_jclr_reg },
|
|
{ "0000101011MMMRRR10000000", "jmp ea", dis_jmp_ea, emu_jmp_ea, match_MMMRRR },
|
|
{ "000011000000aaaaaaaaaaaa", "jmp xxx", dis_jmp_imm, emu_jmp_imm },
|
|
{ "00001111CCCCaaaaaaaaaaaa", "jscc xxx", dis_jscc_imm, emu_jscc_imm },
|
|
{ "0000101111MMMRRR1010CCCC", "jscc ea", dis_jscc_ea, emu_jscc_ea, match_MMMRRR },
|
|
{ "0000101101MMMRRR1S00bbbb", "jsclr #n, [X or Y]:ea, xxxx", dis_jsclr_ea, emu_jsclr_ea, match_MMMRRR },
|
|
{ "0000101100MMMRRR1S00bbbb", "jsclr #n, [X or Y]:aa, xxxx", dis_jsclr_aa, emu_jsclr_aa, match_MMMRRR },
|
|
{ "0000101110pppppp1S0bbbbb", "jsclr #n, [X or Y]:pp, xxxx", dis_jsclr_pp, emu_jsclr_pp },
|
|
{ "0000000111qqqqqq1S0bbbbb", "jsclr #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000101111DDDDDD000bbbbb", "jsclr #n, S, xxxx", dis_jsclr_reg, emu_jsclr_reg },
|
|
{ "0000101001MMMRRR1S10bbbb", "jset #n, [X or Y]:ea, xxxx", dis_jset_ea, emu_jset_ea, match_MMMRRR },
|
|
{ "0000101000MMMRRR1S10bbbb", "jset #n, [X or Y]:aa, xxxx", dis_jset_aa, emu_jset_aa, match_MMMRRR },
|
|
{ "0000101010pppppp1S10bbbb", "jset #n, [X or Y]:pp, xxxx", dis_jset_pp, emu_jset_pp },
|
|
{ "0000000110qqqqqq1S10bbbb", "jset #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000101011DDDDDD0010bbbb", "jset #n, S, xxxx", dis_jset_reg, emu_jset_reg },
|
|
{ "0000101111MMMRRR10000000", "jsr ea", dis_jsr_ea, emu_jsr_ea, match_MMMRRR },
|
|
{ "000011010000aaaaaaaaaaaa", "jsr xxx", dis_jsr_imm, emu_jsr_imm },
|
|
{ "0000101101MMMRRR1S10bbbb", "jsset #n, [X or Y]:ea, xxxx", dis_jsset_ea, emu_jsset_ea, match_MMMRRR },
|
|
{ "0000101100aaaaaa1S10bbbb", "jsset #n, [X or Y]:aa, xxxx", dis_jsset_aa, emu_jsset_aa },
|
|
{ "0000101110pppppp1S1bbbbb", "jsset #n, [X or Y]:pp, xxxx", dis_jsset_pp, emu_jsset_pp },
|
|
{ "0000000111qqqqqq1S1bbbbb", "jsset #n, [X or Y]:qq, xxxx", NULL, NULL },
|
|
{ "0000101111DDDDDD001bbbbb", "jsset #n, S, xxxx", dis_jsset_reg, emu_jsset_reg },
|
|
{ "0000010011000RRR000ddddd", "lra Rn, D", NULL, NULL },
|
|
{ "0000010001000000010ddddd", "lra xxxx, D", NULL, NULL },
|
|
{ "000011000001111010iiiiiD", "lsl #ii, D", NULL, NULL },
|
|
{ "00001100000111100001sssD", "lsl S, D", NULL, NULL },
|
|
{ "000011000001111011iiiiiD", "lsr #ii, D", NULL, NULL },
|
|
{ "00001100000111100011sssD", "lsr S, D", NULL, NULL },
|
|
{ "00000100010MMRRR000ddddd", "lua ea, D", dis_lua, emu_lua },
|
|
{ "0000010000aaaRRRaaaadddd", "lua (Rn + aa), D", dis_lua_rel, emu_lua_rel },
|
|
{ "00000001000sssss11QQdk10", "mac S, #n, D", NULL, NULL },
|
|
{ "000000010100000111qqdk10", "maci #xxxx, S, D", NULL, NULL },
|
|
{ "00000001001001101sdkQQQQ", "mac_s_u S1, S2, D", NULL, NULL },
|
|
{ "00000001000sssss11QQdk11", "macr S1, S2, D", NULL, NULL },
|
|
{ "000000010100000111qqdk11", "macri #xxxx, S, D", NULL, NULL },
|
|
{ "00001100000110111000sssD", "merge S, D", NULL, NULL },
|
|
{ "0000101001110RRR1WDDDDDD", "move X:(Rn + xxxx) <-> R", dis_move_x_long, emu_move_x_long },
|
|
{ "0000101101110RRR1WDDDDDD", "move Y:(Rn + xxxx) <-> R", NULL, NULL },
|
|
{ "0000001aaaaaaRRR1a0WDDDD", "move X:(Rn + xxx) <-> R", dis_move_x_imm, emu_move_x_imm },
|
|
{ "0000001aaaaaaRRR1a1WDDDD", "move Y:(Rn + xxx) <-> R", dis_move_y_imm, emu_move_y_imm },
|
|
{ "00000101W1MMMRRR0s1ddddd", "movec [X or Y]:ea <-> R", dis_movec_ea, emu_movec_ea, match_MMMRRR },
|
|
{ "00000101W0aaaaaa0s1ddddd", "movec [X or Y]:aa <-> R", dis_movec_aa, emu_movec_aa, match_MMMRRR },
|
|
{ "00000100W1eeeeee101ddddd", "movec R1, R2", dis_movec_reg, emu_movec_reg },
|
|
{ "00000101iiiiiiii101ddddd", "movec #xx, D1", dis_movec_imm, emu_movec_imm },
|
|
{ "00000111W1MMMRRR10dddddd", "movem P:ea <-> R", dis_movem_ea, emu_movem_ea, match_MMMRRR },
|
|
{ "00000111W0aaaaaa00dddddd", "movem P:ea <-> R", dis_movem_aa, emu_movem_aa, match_MMMRRR },
|
|
{ "0000100sW1MMMRRR1Spppppp", "movep [X or Y]:ea <-> [X or Y]:pp", dis_movep_23, emu_movep_23, match_MMMRRR },
|
|
{ "00000111W1MMMRRR0Sqqqqqq", "movep [X or Y]:ea <-> X:qq", dis_movep_x_qq, emu_movep_x_qq, match_MMMRRR },
|
|
{ "00000111W0MMMRRR1Sqqqqqq", "movep [X or Y]:ea <-> Y:qq", NULL, NULL, match_MMMRRR },
|
|
{ "0000100sW1MMMRRR01pppppp", "movep [X or Y]:pp <-> P:ea", dis_movep_1, emu_movep_1, match_MMMRRR },
|
|
{ "000000001WMMMRRR0sqqqqqq", "movep [X or Y]:qq <-> P:ea", NULL, NULL, match_MMMRRR },
|
|
{ "0000100sW1dddddd00pppppp", "movep [X or Y]:pp <-> R", dis_movep_0, emu_movep_0 },
|
|
{ "00000100W1dddddd1q0qqqqq", "movep X:qq <-> R", NULL, NULL },
|
|
{ "00000100W1dddddd0q1qqqqq", "movep Y:qq <-> R", NULL, NULL },
|
|
{ "00000001000sssss11QQdk00", "mpy S, #n, D", NULL, NULL },
|
|
{ "00000001001001111sdkQQQQ", "mpy_s_u S1, S2, D", NULL, NULL },
|
|
{ "000000010100000111qqdk00", "mpyi #xxxx, S, D", dis_mpyi, emu_mpyi },
|
|
{ "00000001000sssss11QQdk01", "mpyr S, #n, D", NULL, NULL },
|
|
{ "000000010100000111qqdk01", "mpyri #xxxx, S, D", NULL, NULL },
|
|
{ "000000000000000000000000", "nop", NULL, emu_nop},
|
|
{ "0000000111011RRR0001d101", "norm Rn, D", dis_norm, emu_norm },
|
|
{ "00001100000111100010sssD", "normf S, D", NULL, NULL },
|
|
{ "0000000101iiiiii1000d010", "or #xx, D", NULL, NULL },
|
|
{ "00000001010000001100d010", "or #xxxx, D", dis_or_long, emu_or_long },
|
|
{ "00000000iiiiiiii111110EE", "ori #xx, D", dis_ori, emu_ori },
|
|
{ "000000000000000000000011", "pflush", NULL, NULL },
|
|
{ "000000000000000000000001", "pflushun", NULL, NULL },
|
|
{ "000000000000000000000010", "pfree", NULL, NULL },
|
|
{ "0000101111MMMRRR10000001", "plock ea", NULL, NULL, match_MMMRRR },
|
|
{ "000000000000000000001111", "plockr xxxx", NULL, NULL },
|
|
{ "0000101011MMMRRR10000001", "punlock ea", NULL, NULL, match_MMMRRR },
|
|
{ "000000000000000000001110", "punlockr xxxx", NULL, NULL },
|
|
{ "0000011001MMMRRR0S100000", "rep [X or Y]:ea", dis_rep_ea, emu_rep_ea, match_MMMRRR },
|
|
{ "0000011000aaaaaa0S100000", "rep [X or Y]:aa", dis_rep_aa, emu_rep_aa },
|
|
{ "00000110iiiiiiii1010hhhh", "rep #xxx", dis_rep_imm, emu_rep_imm },
|
|
{ "0000011011dddddd00100000", "rep S", dis_rep_reg, emu_rep_reg },
|
|
{ "000000000000000010000100", "reset", NULL, emu_reset },
|
|
{ "000000000000000000000100", "rti", NULL, emu_rti },
|
|
{ "000000000000000000001100", "rts", NULL, emu_rts },
|
|
{ "000000000000000010000111", "stop", NULL, emu_stop },
|
|
{ "0000000101iiiiii1000d100", "sub #xx, D", dis_sub_imm, emu_sub_imm },
|
|
{ "00000001010000001100d100", "sub #xxxx, D", dis_sub_long, emu_sub_long },
|
|
{ "00000010CCCC00000JJJd000", "tcc S1, D1", dis_tcc, emu_tcc },
|
|
{ "00000011CCCC0ttt0JJJdTTT", "tcc S1,D2 S2,D2", dis_tcc, emu_tcc },
|
|
{ "00000010CCCC1ttt00000TTT", "tcc S2, D2", dis_tcc, emu_tcc },
|
|
{ "000000000000000000000110", "trap", NULL, NULL },
|
|
{ "00000000000000000001CCCC", "trapcc", NULL, NULL },
|
|
{ "0000101S11MMMRRR110i0000", "vsl", NULL, NULL, match_MMMRRR },
|
|
{ "000000000000000010000110", "wait", NULL, emu_wait },
|
|
};
|
|
|
|
static bool matches_initialised;
|
|
static uint32_t nonparallel_matches[ARRAYSIZE(nonparallel_opcodes)][2];
|
|
|
|
/**********************************
|
|
* Emulator kernel
|
|
**********************************/
|
|
|
|
void dsp56k_reset_cpu(dsp_core_t* dsp)
|
|
{
|
|
int i;
|
|
if (!matches_initialised) {
|
|
matches_initialised = true;
|
|
for (i=0; i<ARRAYSIZE(nonparallel_opcodes); i++) {
|
|
const OpcodeEntry t = nonparallel_opcodes[i];
|
|
assert(strlen(t.template) == 24);
|
|
|
|
uint32_t mask = 0;
|
|
uint32_t match = 0;
|
|
int j;
|
|
for (j=0; j<24; j++) {
|
|
if (t.template[j] == '0' || t.template[j] == '1') {
|
|
mask |= 1 << (24-j-1);
|
|
match |= (t.template[j] - '0') << (24-j-1);
|
|
}
|
|
}
|
|
|
|
nonparallel_matches[i][0] = mask;
|
|
nonparallel_matches[i][1] = match;
|
|
}
|
|
}
|
|
|
|
/* Memory */
|
|
memset(dsp->periph, 0, sizeof(dsp->periph));
|
|
memset(dsp->stack, 0, sizeof(dsp->stack));
|
|
memset(dsp->registers, 0, sizeof(dsp->registers));
|
|
|
|
/* Registers */
|
|
dsp->pc = 0x0000;
|
|
dsp->registers[DSP_REG_OMR]=0x02;
|
|
for (i=0;i<8;i++) {
|
|
dsp->registers[DSP_REG_M0+i]=0x00ffff;
|
|
}
|
|
|
|
/* Interruptions */
|
|
memset(dsp->interrupt_is_pending, 0, sizeof(dsp->interrupt_is_pending));
|
|
dsp->interrupt_state = DSP_INTERRUPT_NONE;
|
|
dsp->interrupt_instr_fetch = -1;
|
|
dsp->interrupt_save_pc = -1;
|
|
dsp->interrupt_counter = 0;
|
|
dsp->interrupt_pipeline_count = 0;
|
|
for (i=0;i<5;i++) {
|
|
dsp->interrupt_ipl[i] = 3;
|
|
}
|
|
for (i=5;i<12;i++) {
|
|
dsp->interrupt_ipl[i] = -1;
|
|
}
|
|
|
|
/* Misc */
|
|
dsp->loop_rep = 0;
|
|
|
|
|
|
/* runtime shit */
|
|
|
|
dsp->executing_for_disasm = false;
|
|
// start_time = SDL_GetTicks();
|
|
dsp->num_inst = 0;
|
|
|
|
dsp->exception_debugging = true;
|
|
dsp->disasm_prev_inst_pc = 0xFFFFFFFF;
|
|
}
|
|
|
|
static OpcodeEntry lookup_opcode(uint32_t op) {
|
|
OpcodeEntry r = {0};
|
|
int i;
|
|
for (i=0; i<ARRAYSIZE(nonparallel_opcodes); i++) {
|
|
if ((op & nonparallel_matches[i][0]) == nonparallel_matches[i][1]) {
|
|
if (nonparallel_opcodes[i].match_func
|
|
&& !nonparallel_opcodes[i].match_func(op)) continue;
|
|
if (r.template != NULL) {
|
|
printf("qqq %x %s\n", op, r.template);
|
|
}
|
|
assert(r.template == NULL);
|
|
r = nonparallel_opcodes[i];
|
|
}
|
|
}
|
|
return r;
|
|
}
|
|
|
|
static uint16_t disasm_instruction(dsp_core_t* dsp, dsp_trace_disasm_t mode)
|
|
{
|
|
dsp->disasm_mode = mode;
|
|
if (mode == DSP_TRACE_MODE) {
|
|
if (dsp->disasm_prev_inst_pc == dsp->pc) {
|
|
if (!dsp->disasm_is_looping) {
|
|
printf( "Looping on DSP instruction at PC = $%04x\n", dsp->disasm_prev_inst_pc);
|
|
dsp->disasm_is_looping = true;
|
|
}
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
dsp->disasm_prev_inst_pc = dsp->pc;
|
|
dsp->disasm_is_looping = false;
|
|
|
|
dsp->disasm_cur_inst = dsp56k_read_memory(dsp, DSP_SPACE_P, dsp->pc);
|
|
dsp->disasm_cur_inst_len = 1;
|
|
|
|
dsp->disasm_parallelmove_name[0] = 0;
|
|
|
|
if (dsp->disasm_cur_inst < 0x100000) {
|
|
const OpcodeEntry op = lookup_opcode(dsp->disasm_cur_inst);
|
|
if (op.template) {
|
|
if (op.dis_func) {
|
|
op.dis_func(dsp);
|
|
} else {
|
|
sprintf(dsp->disasm_str_instr, "%s", op.name);
|
|
}
|
|
} else {
|
|
dis_undefined(dsp);
|
|
}
|
|
} else {
|
|
dis_pm(dsp);
|
|
sprintf(dsp->disasm_str_instr, "%s %s",
|
|
disasm_opcodes_alu[dsp->disasm_cur_inst & BITMASK(8)], dsp->disasm_parallelmove_name);
|
|
}
|
|
return dsp->disasm_cur_inst_len;
|
|
}
|
|
|
|
static void disasm_reg_save(dsp_core_t* dsp)
|
|
{
|
|
memcpy(dsp->disasm_registers_save, dsp->registers , sizeof(dsp->disasm_registers_save));
|
|
#ifdef DSP_DISASM_REG_PC
|
|
dsp->pc_save = dsp->pc;
|
|
#endif
|
|
}
|
|
|
|
static void disasm_reg_compare(dsp_core_t* dsp)
|
|
{
|
|
int i;
|
|
bool bRegA = false;
|
|
bool bRegB = false;
|
|
|
|
for (i=4; i<64; i++) {
|
|
if (dsp->disasm_registers_save[i] == dsp->registers[i]) {
|
|
continue;
|
|
}
|
|
|
|
switch(i) {
|
|
case DSP_REG_X0:
|
|
case DSP_REG_X1:
|
|
case DSP_REG_Y0:
|
|
case DSP_REG_Y1:
|
|
printf("\tReg: %s $%06x -> $%06x\n",
|
|
registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]);
|
|
break;
|
|
case DSP_REG_R0:
|
|
case DSP_REG_R1:
|
|
case DSP_REG_R2:
|
|
case DSP_REG_R3:
|
|
case DSP_REG_R4:
|
|
case DSP_REG_R5:
|
|
case DSP_REG_R6:
|
|
case DSP_REG_R7:
|
|
case DSP_REG_M0:
|
|
case DSP_REG_M1:
|
|
case DSP_REG_M2:
|
|
case DSP_REG_M3:
|
|
case DSP_REG_M4:
|
|
case DSP_REG_M5:
|
|
case DSP_REG_M6:
|
|
case DSP_REG_M7:
|
|
case DSP_REG_N0:
|
|
case DSP_REG_N1:
|
|
case DSP_REG_N2:
|
|
case DSP_REG_N3:
|
|
case DSP_REG_N4:
|
|
case DSP_REG_N5:
|
|
case DSP_REG_N6:
|
|
case DSP_REG_N7:
|
|
case DSP_REG_SR:
|
|
case DSP_REG_LA:
|
|
case DSP_REG_LC:
|
|
printf("\tReg: %s $%04x -> $%04x\n",
|
|
registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]);
|
|
break;
|
|
case DSP_REG_OMR:
|
|
case DSP_REG_SP:
|
|
case DSP_REG_SSH:
|
|
case DSP_REG_SSL:
|
|
printf("\tReg: %s $%02x -> $%02x\n",
|
|
registers_name[i], dsp->disasm_registers_save[i], dsp->registers[i]);
|
|
break;
|
|
case DSP_REG_A0:
|
|
case DSP_REG_A1:
|
|
case DSP_REG_A2:
|
|
if (bRegA == false) {
|
|
printf("\tReg: a $%02x:%06x:%06x -> $%02x:%06x:%06x\n",
|
|
dsp->disasm_registers_save[DSP_REG_A2], dsp->disasm_registers_save[DSP_REG_A1], dsp->disasm_registers_save[DSP_REG_A0],
|
|
dsp->registers[DSP_REG_A2], dsp->registers[DSP_REG_A1], dsp->registers[DSP_REG_A0]
|
|
);
|
|
bRegA = true;
|
|
}
|
|
break;
|
|
case DSP_REG_B0:
|
|
case DSP_REG_B1:
|
|
case DSP_REG_B2:
|
|
if (bRegB == false) {
|
|
printf("\tReg: b $%02x:%06x:%06x -> $%02x:%06x:%06x\n",
|
|
dsp->disasm_registers_save[DSP_REG_B2], dsp->disasm_registers_save[DSP_REG_B1], dsp->disasm_registers_save[DSP_REG_B0],
|
|
dsp->registers[DSP_REG_B2], dsp->registers[DSP_REG_B1], dsp->registers[DSP_REG_B0]
|
|
);
|
|
bRegB = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
#ifdef DSP_DISASM_REG_PC
|
|
if (pc_save != dsp->pc) {
|
|
printf("\tReg: pc $%04x -> $%04x\n", pc_save, dsp->pc);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
static const char* disasm_get_instruction_text(dsp_core_t* dsp)
|
|
{
|
|
const int len = sizeof(dsp->disasm_str_instr);
|
|
// uint64_t count, cycles;
|
|
// uint16_t cycle_diff;
|
|
// float percentage;
|
|
int offset;
|
|
|
|
if (dsp->disasm_is_looping) {
|
|
dsp->disasm_str_instr2[0] = 0;
|
|
}
|
|
if (dsp->disasm_cur_inst_len == 1) {
|
|
offset = sprintf(dsp->disasm_str_instr2, "p:%04x %06x (%02d cyc) %-*s\n", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, dsp->instr_cycle, len, dsp->disasm_str_instr);
|
|
} else {
|
|
offset = sprintf(dsp->disasm_str_instr2, "p:%04x %06x %06x (%02d cyc) %-*s\n", dsp->disasm_prev_inst_pc, dsp->disasm_cur_inst, read_memory_p(dsp, dsp->disasm_prev_inst_pc + 1), dsp->instr_cycle, len, dsp->disasm_str_instr);
|
|
}
|
|
// if (offset > 2 && Profile_DspAddressData(dsp->disasm_prev_inst_pc, &percentage, &count, &cycles, &cycle_diff)) {
|
|
// offset -= 2;
|
|
// sprintf(str_instr2+offset, "%5.2f%% (%"PRId64", %"PRId64", %d)\n",
|
|
// percentage, count, cycles, cycle_diff);
|
|
// }
|
|
return dsp->disasm_str_instr2;
|
|
}
|
|
|
|
/**
|
|
* Execute one instruction in trace mode at a given PC address.
|
|
* */
|
|
uint16_t dsp56k_execute_one_disasm_instruction(dsp_core_t* dsp, FILE *out, uint32_t pc)
|
|
{
|
|
dsp_core_t dsp_core_save;
|
|
|
|
/* Set DSP in disasm mode */
|
|
dsp->executing_for_disasm = true;
|
|
|
|
/* Save DSP context before executing instruction */
|
|
memcpy(&dsp_core_save, dsp, sizeof(dsp_core_t));
|
|
|
|
/* execute and disasm instruction */
|
|
dsp->pc = pc;
|
|
|
|
/* Disasm instruction */
|
|
uint16_t instruction_length = disasm_instruction(dsp, DSP_DISASM_MODE) - 1;
|
|
|
|
/* Execute instruction at address given in parameter to get the number of cycles it takes */
|
|
dsp56k_execute_instruction(dsp);
|
|
|
|
fprintf(out, "%s", disasm_get_instruction_text(dsp));
|
|
|
|
/* Restore DSP context after executing instruction */
|
|
memcpy(dsp, &dsp_core_save, sizeof(dsp_core_t));
|
|
|
|
/* Unset DSP in disasm mode */
|
|
dsp->executing_for_disasm = false;
|
|
|
|
return instruction_length;
|
|
}
|
|
|
|
void dsp56k_execute_instruction(dsp_core_t* dsp)
|
|
{
|
|
uint32_t disasm_return = 0;
|
|
dsp->disasm_memory_ptr = 0;
|
|
|
|
/* Decode and execute current instruction */
|
|
dsp->cur_inst = read_memory_p(dsp, dsp->pc);
|
|
|
|
/* Initialize instruction size and cycle counter */
|
|
dsp->cur_inst_len = 1;
|
|
dsp->instr_cycle = 2;
|
|
|
|
/* Disasm current instruction ? (trace mode only) */
|
|
if (TRACE_DSP_DISASM) {
|
|
/* Call disasm_instruction only when DSP is called in trace mode */
|
|
if (!dsp->executing_for_disasm) {
|
|
disasm_return = disasm_instruction(dsp, DSP_TRACE_MODE);
|
|
|
|
if (disasm_return) {
|
|
printf( "%s", disasm_get_instruction_text(dsp));
|
|
}
|
|
if (disasm_return != 0 && TRACE_DSP_DISASM_REG) {
|
|
/* DSP regs trace enabled only if DSP DISASM is enabled */
|
|
disasm_reg_save(dsp);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (dsp->cur_inst < 0x100000) {
|
|
const OpcodeEntry op = lookup_opcode(dsp->cur_inst);
|
|
if (op.emu_func) {
|
|
op.emu_func(dsp);
|
|
} else {
|
|
printf("%x - %s\n", dsp->cur_inst, op.name);
|
|
emu_undefined(dsp);
|
|
}
|
|
} else {
|
|
/* Do parallel move read */
|
|
opcodes_parmove[(dsp->cur_inst>>20) & BITMASK(4)](dsp);
|
|
}
|
|
|
|
/* Disasm current instruction ? (trace mode only) */
|
|
if (TRACE_DSP_DISASM) {
|
|
/* Display only when DSP is called in trace mode */
|
|
if (!dsp->executing_for_disasm) {
|
|
if (disasm_return != 0) {
|
|
// printf( "%s", disasm_get_instruction_text(dsp));
|
|
|
|
/* DSP regs trace enabled only if DSP DISASM is enabled */
|
|
if (TRACE_DSP_DISASM_REG)
|
|
disasm_reg_compare(dsp);
|
|
|
|
if (TRACE_DSP_DISASM_MEM) {
|
|
/* 1 memory change to display ? */
|
|
if (dsp->disasm_memory_ptr == 1)
|
|
printf( "\t%s\n", dsp->str_disasm_memory[0]);
|
|
/* 2 memory changes to display ? */
|
|
else if (dsp->disasm_memory_ptr == 2) {
|
|
printf( "\t%s\n", dsp->str_disasm_memory[0]);
|
|
printf( "\t%s\n", dsp->str_disasm_memory[1]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Process the PC */
|
|
dsp_postexecute_update_pc(dsp);
|
|
|
|
/* Process Interrupts */
|
|
dsp_postexecute_interrupts(dsp);
|
|
|
|
#ifdef DSP_COUNT_IPS
|
|
++dsp->num_inst;
|
|
if ((dsp->num_inst & 63) == 0) {
|
|
/* Evaluate time after <N> instructions have been executed to avoid asking too frequently */
|
|
uint32_t cur_time = SDL_GetTicks();
|
|
if (cur_time-start_time>1000) {
|
|
printf( "Dsp: %d i/s\n", (dsp->num_inst*1000)/(cur_time-start_time));
|
|
start_time=cur_time;
|
|
dsp->num_inst=0;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/**********************************
|
|
* Update the PC
|
|
**********************************/
|
|
|
|
static void dsp_postexecute_update_pc(dsp_core_t* dsp)
|
|
{
|
|
/* When running a REP, PC must stay on the current instruction */
|
|
if (dsp->loop_rep) {
|
|
/* Is PC on the instruction to repeat ? */
|
|
if (dsp->pc_on_rep==0) {
|
|
--dsp->registers[DSP_REG_LC];
|
|
dsp->registers[DSP_REG_LC] &= BITMASK(16);
|
|
|
|
if (dsp->registers[DSP_REG_LC] > 0) {
|
|
dsp->cur_inst_len = 0; /* Stay on this instruction */
|
|
} else {
|
|
dsp->loop_rep = 0;
|
|
dsp->registers[DSP_REG_LC] = dsp->registers[DSP_REG_LCSAVE];
|
|
}
|
|
} else {
|
|
/* Init LC at right value */
|
|
if (dsp->registers[DSP_REG_LC] == 0) {
|
|
dsp->registers[DSP_REG_LC] = 0x010000;
|
|
}
|
|
dsp->pc_on_rep = 0;
|
|
}
|
|
}
|
|
|
|
/* Normal execution, go to next instruction */
|
|
dsp->pc += dsp->cur_inst_len;
|
|
|
|
/* When running a DO loop, we test the end of loop with the */
|
|
/* updated PC, pointing to last instruction of the loop */
|
|
if (dsp->registers[DSP_REG_SR] & (1<<DSP_SR_LF)) {
|
|
|
|
/* Did we execute the last instruction in loop ? */
|
|
if (dsp->pc == dsp->registers[DSP_REG_LA] + 1) {
|
|
--dsp->registers[DSP_REG_LC];
|
|
dsp->registers[DSP_REG_LC] &= BITMASK(16);
|
|
|
|
if (dsp->registers[DSP_REG_LC] == 0) {
|
|
/* end of loop */
|
|
uint32_t saved_pc, saved_sr;
|
|
|
|
dsp_stack_pop(dsp, &saved_pc, &saved_sr);
|
|
dsp->registers[DSP_REG_SR] &= 0x7f;
|
|
dsp->registers[DSP_REG_SR] |= saved_sr & (1<<DSP_SR_LF);
|
|
dsp_stack_pop(dsp, &dsp->registers[DSP_REG_LA], &dsp->registers[DSP_REG_LC]);
|
|
} else {
|
|
/* Loop one more time */
|
|
dsp->pc = dsp->registers[DSP_REG_SSH];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
* Interrupts
|
|
**********************************/
|
|
|
|
/* Post a new interrupt to the interrupt table */
|
|
void dsp56k_add_interrupt(dsp_core_t* dsp, uint16_t inter)
|
|
{
|
|
/* detect if this interrupt is used or not */
|
|
if (dsp->interrupt_ipl[inter] == -1)
|
|
return;
|
|
|
|
/* add this interrupt to the pending interrupts table */
|
|
if (dsp->interrupt_is_pending[inter] == 0) {
|
|
dsp->interrupt_is_pending[inter] = 1;
|
|
dsp->interrupt_counter ++;
|
|
}
|
|
}
|
|
|
|
static void dsp_postexecute_interrupts(dsp_core_t* dsp)
|
|
{
|
|
uint32_t index, instr, i;
|
|
int32_t ipl_to_raise, ipl_sr;
|
|
|
|
/* REP is not interruptible */
|
|
if (dsp->loop_rep) {
|
|
return;
|
|
}
|
|
|
|
/* A fast interrupt can not be interrupted. */
|
|
if (dsp->interrupt_state == DSP_INTERRUPT_DISABLED) {
|
|
|
|
switch (dsp->interrupt_pipeline_count) {
|
|
case 5:
|
|
dsp->interrupt_pipeline_count --;
|
|
return;
|
|
case 4:
|
|
/* Prefetch interrupt instruction 1 */
|
|
dsp->interrupt_save_pc = dsp->pc;
|
|
dsp->pc = dsp->interrupt_instr_fetch;
|
|
|
|
/* is it a LONG interrupt ? */
|
|
instr = read_memory_p(dsp, dsp->interrupt_instr_fetch);
|
|
if ( ((instr & 0xfff000) == 0x0d0000) || ((instr & 0xffc0ff) == 0x0bc080) ) {
|
|
dsp->interrupt_state = DSP_INTERRUPT_LONG;
|
|
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
|
|
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_T) |
|
|
(1<<DSP_SR_S1)|(1<<DSP_SR_S0) |
|
|
(1<<DSP_SR_I0)|(1<<DSP_SR_I1));
|
|
dsp->registers[DSP_REG_SR] |= dsp->interrupt_ipl_to_raise<<DSP_SR_I0;
|
|
}
|
|
dsp->interrupt_pipeline_count --;
|
|
return;
|
|
case 3:
|
|
/* Prefetch interrupt instruction 2 */
|
|
if (dsp->pc == dsp->interrupt_instr_fetch+1) {
|
|
instr = read_memory_p(dsp, dsp->pc);
|
|
if ( ((instr & 0xfff000) == 0x0d0000) || ((instr & 0xffc0ff) == 0x0bc080) ) {
|
|
dsp->interrupt_state = DSP_INTERRUPT_LONG;
|
|
dsp_stack_push(dsp, dsp->interrupt_save_pc, dsp->registers[DSP_REG_SR], 0);
|
|
dsp->registers[DSP_REG_SR] &= BITMASK(16)-((1<<DSP_SR_LF)|(1<<DSP_SR_T) |
|
|
(1<<DSP_SR_S1)|(1<<DSP_SR_S0) |
|
|
(1<<DSP_SR_I0)|(1<<DSP_SR_I1));
|
|
dsp->registers[DSP_REG_SR] |= dsp->interrupt_ipl_to_raise<<DSP_SR_I0;
|
|
}
|
|
}
|
|
dsp->interrupt_pipeline_count --;
|
|
return;
|
|
case 2:
|
|
/* 1 instruction executed after interrupt */
|
|
/* before re enable interrupts */
|
|
/* Was it a FAST interrupt ? */
|
|
if (dsp->pc == dsp->interrupt_instr_fetch+2) {
|
|
dsp->pc = dsp->interrupt_save_pc;
|
|
}
|
|
dsp->interrupt_pipeline_count --;
|
|
return;
|
|
case 1:
|
|
/* Last instruction executed after interrupt */
|
|
/* before re enable interrupts */
|
|
dsp->interrupt_pipeline_count --;
|
|
return;
|
|
case 0:
|
|
/* Re enable interrupts */
|
|
/* All 6 instruction are done, Interrupts can be enabled again */
|
|
dsp->interrupt_save_pc = -1;
|
|
dsp->interrupt_instr_fetch = -1;
|
|
dsp->interrupt_state = DSP_INTERRUPT_NONE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Trace Interrupt ? */
|
|
if (dsp->registers[DSP_REG_SR] & (1<<DSP_SR_T)) {
|
|
dsp56k_add_interrupt(dsp, DSP_INTER_TRACE);
|
|
}
|
|
|
|
/* No interrupt to execute */
|
|
if (dsp->interrupt_counter == 0) {
|
|
return;
|
|
}
|
|
|
|
/* search for an interrupt */
|
|
ipl_sr = (dsp->registers[DSP_REG_SR]>>DSP_SR_I0) & BITMASK(2);
|
|
index = 0xffff;
|
|
ipl_to_raise = -1;
|
|
|
|
/* Arbitrate between all pending interrupts */
|
|
for (i=0; i<12; i++) {
|
|
if (dsp->interrupt_is_pending[i] == 1) {
|
|
|
|
/* level 3 interrupt ? */
|
|
if (dsp->interrupt_ipl[i] == 3) {
|
|
index = i;
|
|
break;
|
|
}
|
|
|
|
/* level 0, 1 ,2 interrupt ? */
|
|
/* if interrupt is masked in SR, don't process it */
|
|
if (dsp->interrupt_ipl[i] < ipl_sr)
|
|
continue;
|
|
|
|
/* if interrupt is lower or equal than current arbitrated interrupt */
|
|
if (dsp->interrupt_ipl[i] <= ipl_to_raise)
|
|
continue;
|
|
|
|
/* save current arbitrated interrupt */
|
|
index = i;
|
|
ipl_to_raise = dsp->interrupt_ipl[i];
|
|
}
|
|
}
|
|
|
|
/* If there's no interrupt to process, return */
|
|
if (index == 0xffff) {
|
|
return;
|
|
}
|
|
|
|
/* remove this interrupt from the pending interrupts table */
|
|
dsp->interrupt_is_pending[index] = 0;
|
|
dsp->interrupt_counter --;
|
|
|
|
/* process arbritrated interrupt */
|
|
ipl_to_raise = dsp->interrupt_ipl[index] + 1;
|
|
if (ipl_to_raise > 3) {
|
|
ipl_to_raise = 3;
|
|
}
|
|
|
|
dsp->interrupt_instr_fetch = dsp_interrupt[index].vectorAddr;
|
|
dsp->interrupt_pipeline_count = 5;
|
|
dsp->interrupt_state = DSP_INTERRUPT_DISABLED;
|
|
dsp->interrupt_ipl_to_raise = ipl_to_raise;
|
|
|
|
DPRINTF("Dsp interrupt: %s\n", dsp_interrupt[index].name);
|
|
|
|
/* SSI receive data with exception ? */
|
|
if (dsp->interrupt_instr_fetch == 0xe) {
|
|
// dsp->periph[DSP_SPACE_X][DSP_SSI_SR] &= 0xff-(1<<DSP_SSI_SR_ROE);
|
|
assert(false);
|
|
}
|
|
|
|
/* SSI transmit data with exception ? */
|
|
else if (dsp->interrupt_instr_fetch == 0x12) {
|
|
// dsp->periph[DSP_SPACE_X][DSP_SSI_SR] &= 0xff-(1<<DSP_SSI_SR_TUE);
|
|
assert(false);
|
|
}
|
|
|
|
/* host command ? */
|
|
else if (dsp->interrupt_instr_fetch == 0xff) {
|
|
/* Clear HC and HCP interrupt */
|
|
// dsp->periph[DSP_SPACE_X][DSP_HOST_HSR] &= 0xff - (1<<DSP_HOST_HSR_HCP);
|
|
// dsp->hostport[CPU_HOST_CVR] &= 0xff - (1<<CPU_HOST_CVR_HC);
|
|
|
|
// dsp->interrupt_instr_fetch = dsp->hostport[CPU_HOST_CVR] & BITMASK(5);
|
|
// dsp->interrupt_instr_fetch *= 2;
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
* Read/Write memory functions
|
|
**********************************/
|
|
|
|
static uint32_t read_memory_p(dsp_core_t* dsp, uint32_t address)
|
|
{
|
|
assert((address & 0xFF000000) == 0);
|
|
assert(address < DSP_PRAM_SIZE);
|
|
uint32_t r = ldl_le_p(&dsp->pram[address]);
|
|
assert((r & 0xFF000000) == 0);
|
|
return r;
|
|
}
|
|
|
|
uint32_t dsp56k_read_memory(dsp_core_t* dsp, int space, uint32_t address)
|
|
{
|
|
assert((address & 0xFF000000) == 0);
|
|
|
|
if (space == DSP_SPACE_X) {
|
|
if (address >= DSP_PERIPH_BASE) {
|
|
assert(dsp->read_peripheral);
|
|
return dsp->read_peripheral(dsp, address);
|
|
} else if (address >= DSP_MIXBUFFER_BASE && address < DSP_MIXBUFFER_BASE+DSP_MIXBUFFER_SIZE) {
|
|
return dsp->mixbuffer[address-DSP_MIXBUFFER_BASE];
|
|
} else {
|
|
assert(address < DSP_XRAM_SIZE);
|
|
return dsp->xram[address];
|
|
}
|
|
} else if (space == DSP_SPACE_Y) {
|
|
assert(address < DSP_YRAM_SIZE);
|
|
return dsp->yram[address];
|
|
} else if (space == DSP_SPACE_P) {
|
|
return read_memory_p(dsp, address);
|
|
} else {
|
|
assert(false);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void dsp56k_write_memory(dsp_core_t* dsp, int space, uint32_t address, uint32_t value)
|
|
{
|
|
assert((value & 0xFF000000) == 0);
|
|
assert((address & 0xFF000000) == 0);
|
|
|
|
if (TRACE_DSP_DISASM_MEM)
|
|
write_memory_disasm(dsp, space, address, value);
|
|
else
|
|
write_memory_raw(dsp, space, address, value);
|
|
}
|
|
|
|
static void write_memory_raw(dsp_core_t* dsp, int space, uint32_t address, uint32_t value)
|
|
{
|
|
assert((value & 0xFF000000) == 0);
|
|
assert((address & 0xFF000000) == 0);
|
|
|
|
if (space == DSP_SPACE_X) {
|
|
if (address >= DSP_PERIPH_BASE) {
|
|
assert(dsp->write_peripheral);
|
|
dsp->write_peripheral(dsp, address, value);
|
|
return;
|
|
} else if (address >= DSP_MIXBUFFER_BASE && address < DSP_MIXBUFFER_BASE+DSP_MIXBUFFER_SIZE) {
|
|
dsp->mixbuffer[address-DSP_MIXBUFFER_BASE] = value;
|
|
} else {
|
|
assert(address < DSP_XRAM_SIZE);
|
|
dsp->xram[address] = value;
|
|
}
|
|
} else if (space == DSP_SPACE_Y) {
|
|
assert(address < DSP_YRAM_SIZE);
|
|
dsp->yram[address] = value;
|
|
} else if (space == DSP_SPACE_P) {
|
|
assert(address < DSP_PRAM_SIZE);
|
|
stl_le_p(&dsp->pram[address], value);
|
|
} else {
|
|
assert(false);
|
|
}
|
|
}
|
|
|
|
static uint32_t read_memory_disasm(dsp_core_t* dsp, int space, uint32_t address)
|
|
{
|
|
return dsp56k_read_memory(dsp, space, address);
|
|
}
|
|
|
|
static void write_memory_disasm(dsp_core_t* dsp, int space, uint32_t address, uint32_t value)
|
|
{
|
|
uint32_t oldvalue, curvalue;
|
|
char space_c;
|
|
|
|
oldvalue = read_memory_disasm(dsp, space, address);
|
|
|
|
write_memory_raw(dsp, space, address, value);
|
|
|
|
switch(space) {
|
|
case DSP_SPACE_X:
|
|
space_c = 'x';
|
|
break;
|
|
case DSP_SPACE_Y:
|
|
space_c = 'y';
|
|
break;
|
|
case DSP_SPACE_P:
|
|
space_c = 'p';
|
|
break;
|
|
default:
|
|
assert(false);
|
|
}
|
|
|
|
curvalue = read_memory_disasm(dsp, space, address);
|
|
if (dsp->disasm_memory_ptr < ARRAYSIZE(dsp->str_disasm_memory)) {
|
|
sprintf(dsp->str_disasm_memory[dsp->disasm_memory_ptr], "Mem: %c:0x%04x 0x%06x -> 0x%06x", space_c, address, oldvalue, curvalue);
|
|
dsp->disasm_memory_ptr ++;
|
|
}
|
|
}
|
|
|
|
static void dsp_write_reg(dsp_core_t* dsp, uint32_t numreg, uint32_t value)
|
|
{
|
|
uint32_t stack_error;
|
|
|
|
switch (numreg) {
|
|
case DSP_REG_A:
|
|
dsp->registers[DSP_REG_A0] = 0;
|
|
dsp->registers[DSP_REG_A1] = value;
|
|
dsp->registers[DSP_REG_A2] = value & (1<<23) ? 0xff : 0x0;
|
|
break;
|
|
case DSP_REG_B:
|
|
dsp->registers[DSP_REG_B0] = 0;
|
|
dsp->registers[DSP_REG_B1] = value;
|
|
dsp->registers[DSP_REG_B2] = value & (1<<23) ? 0xff : 0x0;
|
|
break;
|
|
case DSP_REG_OMR:
|
|
dsp->registers[DSP_REG_OMR] = value & 0xc7;
|
|
break;
|
|
case DSP_REG_SR:
|
|
dsp->registers[DSP_REG_SR] = value & 0xaf7f;
|
|
break;
|
|
case DSP_REG_SP:
|
|
stack_error = dsp->registers[DSP_REG_SP] & (3<<DSP_SP_SE);
|
|
if ((stack_error==0) && (value & (3<<DSP_SP_SE))) {
|
|
/* Stack underflow or overflow detected, raise interrupt */
|
|
dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR);
|
|
dsp->registers[DSP_REG_SP] = value & (3<<DSP_SP_SE);
|
|
if (!dsp->executing_for_disasm) {
|
|
printf( "Dsp: Stack Overflow or Underflow\n");
|
|
}
|
|
if (dsp->exception_debugging) {
|
|
assert(false);
|
|
}
|
|
} else {
|
|
dsp->registers[DSP_REG_SP] = value & BITMASK(6);
|
|
}
|
|
dsp_compute_ssh_ssl(dsp);
|
|
break;
|
|
case DSP_REG_SSH:
|
|
dsp_stack_push(dsp, value, 0, 1);
|
|
break;
|
|
case DSP_REG_SSL:
|
|
numreg = dsp->registers[DSP_REG_SP] & BITMASK(4);
|
|
if (numreg == 0) {
|
|
value = 0;
|
|
}
|
|
dsp->stack[1][numreg] = value & BITMASK(16);
|
|
dsp->registers[DSP_REG_SSL] = value & BITMASK(16);
|
|
break;
|
|
default:
|
|
dsp->registers[numreg] = value;
|
|
dsp->registers[numreg] &= BITMASK(registers_mask[numreg]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**********************************
|
|
* Stack push/pop
|
|
**********************************/
|
|
|
|
static void dsp_stack_push(dsp_core_t* dsp, uint32_t curpc, uint32_t cursr, uint16_t sshOnly)
|
|
{
|
|
uint32_t stack_error, underflow, stack;
|
|
|
|
stack_error = dsp->registers[DSP_REG_SP] & (1<<DSP_SP_SE);
|
|
underflow = dsp->registers[DSP_REG_SP] & (1<<DSP_SP_UF);
|
|
stack = (dsp->registers[DSP_REG_SP] & BITMASK(4)) + 1;
|
|
|
|
|
|
if ((stack_error==0) && (stack & (1<<DSP_SP_SE))) {
|
|
/* Stack full, raise interrupt */
|
|
dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR);
|
|
if (!dsp->executing_for_disasm)
|
|
printf("Dsp: Stack Overflow\n");
|
|
if (dsp->exception_debugging)
|
|
assert(false);
|
|
}
|
|
|
|
dsp->registers[DSP_REG_SP] = (underflow | stack_error | stack) & BITMASK(6);
|
|
stack &= BITMASK(4);
|
|
|
|
if (stack) {
|
|
/* SSH part */
|
|
dsp->stack[0][stack] = curpc & BITMASK(16);
|
|
/* SSL part, if instruction is not like "MOVEC xx, SSH" */
|
|
if (sshOnly == 0) {
|
|
dsp->stack[1][stack] = cursr & BITMASK(16);
|
|
}
|
|
} else {
|
|
dsp->stack[0][0] = 0;
|
|
dsp->stack[1][0] = 0;
|
|
}
|
|
|
|
/* Update SSH and SSL registers */
|
|
dsp->registers[DSP_REG_SSH] = dsp->stack[0][stack];
|
|
dsp->registers[DSP_REG_SSL] = dsp->stack[1][stack];
|
|
}
|
|
|
|
static void dsp_stack_pop(dsp_core_t* dsp, uint32_t *newpc, uint32_t *newsr)
|
|
{
|
|
uint32_t stack_error, underflow, stack;
|
|
|
|
stack_error = dsp->registers[DSP_REG_SP] & (1<<DSP_SP_SE);
|
|
underflow = dsp->registers[DSP_REG_SP] & (1<<DSP_SP_UF);
|
|
stack = (dsp->registers[DSP_REG_SP] & BITMASK(4)) - 1;
|
|
|
|
if ((stack_error==0) && (stack & (1<<DSP_SP_SE))) {
|
|
/* Stack empty*/
|
|
dsp56k_add_interrupt(dsp, DSP_INTER_STACK_ERROR);
|
|
if (!dsp->executing_for_disasm)
|
|
printf("Dsp: Stack underflow\n");
|
|
if (dsp->exception_debugging)
|
|
assert(false);
|
|
}
|
|
|
|
dsp->registers[DSP_REG_SP] = (underflow | stack_error | stack) & BITMASK(6);
|
|
stack &= BITMASK(4);
|
|
*newpc = dsp->registers[DSP_REG_SSH];
|
|
*newsr = dsp->registers[DSP_REG_SSL];
|
|
|
|
dsp->registers[DSP_REG_SSH] = dsp->stack[0][stack];
|
|
dsp->registers[DSP_REG_SSL] = dsp->stack[1][stack];
|
|
}
|
|
|
|
static void dsp_compute_ssh_ssl(dsp_core_t* dsp)
|
|
{
|
|
uint32_t stack;
|
|
|
|
stack = dsp->registers[DSP_REG_SP];
|
|
stack &= BITMASK(4);
|
|
dsp->registers[DSP_REG_SSH] = dsp->stack[0][stack];
|
|
dsp->registers[DSP_REG_SSL] = dsp->stack[1][stack];
|
|
}
|
|
|
|
|
|
|
|
/**********************************
|
|
* 56bit arithmetic
|
|
**********************************/
|
|
|
|
/* source,dest[0] is 55:48 */
|
|
/* source,dest[1] is 47:24 */
|
|
/* source,dest[2] is 23:00 */
|
|
|
|
static uint16_t dsp_abs56(uint32_t *dest)
|
|
{
|
|
uint32_t zerodest[3];
|
|
uint16_t newsr;
|
|
|
|
/* D=|D| */
|
|
|
|
if (dest[0] & (1<<7)) {
|
|
zerodest[0] = zerodest[1] = zerodest[2] = 0;
|
|
|
|
newsr = dsp_sub56(dest, zerodest);
|
|
|
|
dest[0] = zerodest[0];
|
|
dest[1] = zerodest[1];
|
|
dest[2] = zerodest[2];
|
|
} else {
|
|
newsr = 0;
|
|
}
|
|
|
|
return newsr;
|
|
}
|
|
|
|
static uint16_t dsp_asl56(uint32_t *dest, int n)
|
|
{
|
|
/* Shift left dest n bits: D<<=n */
|
|
|
|
uint64_t dest_v = dest[2] | ((uint64_t)dest[1] << 24) | ((uint64_t)dest[0] << 48);
|
|
|
|
uint32_t carry = (dest_v >> (56-n)) & 1;
|
|
|
|
uint64_t dest_s = dest_v << n;
|
|
dest[2] = dest_s & BITMASK(24);
|
|
dest[1] = (dest_s >> 24) & BITMASK(24);
|
|
dest[0] = (dest_s >> 48) & BITMASK(8);
|
|
|
|
uint32_t overflow = (dest_v >> (56-n)) != 0;
|
|
uint32_t v = ((dest_v >> 55) & 1) != ((dest_s >> 55) & 1);
|
|
|
|
return (overflow<<DSP_SR_L)|(v<<DSP_SR_V)|(carry<<DSP_SR_C);
|
|
}
|
|
|
|
static uint16_t dsp_asr56(uint32_t *dest, int n)
|
|
{
|
|
/* Shift right dest n bits: D>>=n */
|
|
|
|
uint64_t dest_v = dest[2] | ((uint64_t)dest[1] << 24) | ((uint64_t)dest[0] << 48);
|
|
|
|
uint16_t carry = (dest_v >> (n-1)) & 1;
|
|
|
|
dest_v >>= n;
|
|
dest[2] = dest_v & BITMASK(24);
|
|
dest[1] = (dest_v >> 24) & BITMASK(24);
|
|
dest[0] = (dest_v >> 48) & BITMASK(8);
|
|
|
|
return (carry<<DSP_SR_C);
|
|
}
|
|
|
|
static uint16_t dsp_add56(uint32_t *source, uint32_t *dest)
|
|
{
|
|
uint16_t overflow, carry, flg_s, flg_d, flg_r;
|
|
|
|
flg_s = (source[0]>>7) & 1;
|
|
flg_d = (dest[0]>>7) & 1;
|
|
|
|
/* Add source to dest: D = D+S */
|
|
dest[2] += source[2];
|
|
dest[1] += source[1]+((dest[2]>>24) & 1);
|
|
dest[0] += source[0]+((dest[1]>>24) & 1);
|
|
|
|
carry = (dest[0]>>8) & 1;
|
|
|
|
dest[2] &= BITMASK(24);
|
|
dest[1] &= BITMASK(24);
|
|
dest[0] &= BITMASK(8);
|
|
|
|
flg_r = (dest[0]>>7) & 1;
|
|
|
|
/*set overflow*/
|
|
overflow = (flg_s ^ flg_r) & (flg_d ^ flg_r);
|
|
|
|
return (overflow<<DSP_SR_L)|(overflow<<DSP_SR_V)|(carry<<DSP_SR_C);
|
|
}
|
|
|
|
static uint16_t dsp_sub56(uint32_t *source, uint32_t *dest)
|
|
{
|
|
uint16_t overflow, carry, flg_s, flg_d, flg_r, dest_save;
|
|
|
|
dest_save = dest[0];
|
|
|
|
/* Subtract source from dest: D = D-S */
|
|
dest[2] -= source[2];
|
|
dest[1] -= source[1]+((dest[2]>>24) & 1);
|
|
dest[0] -= source[0]+((dest[1]>>24) & 1);
|
|
|
|
carry = (dest[0]>>8) & 1;
|
|
|
|
dest[2] &= BITMASK(24);
|
|
dest[1] &= BITMASK(24);
|
|
dest[0] &= BITMASK(8);
|
|
|
|
flg_s = (source[0]>>7) & 1;
|
|
flg_d = (dest_save>>7) & 1;
|
|
flg_r = (dest[0]>>7) & 1;
|
|
|
|
/* set overflow */
|
|
overflow = (flg_s ^ flg_d) & (flg_r ^ flg_d);
|
|
|
|
return (overflow<<DSP_SR_L)|(overflow<<DSP_SR_V)|(carry<<DSP_SR_C);
|
|
}
|
|
|
|
static void dsp_mul56(uint32_t source1, uint32_t source2, uint32_t *dest, uint8_t signe)
|
|
{
|
|
uint32_t part[4], zerodest[3], value;
|
|
|
|
/* Multiply: D = S1*S2 */
|
|
if (source1 & (1<<23)) {
|
|
signe ^= 1;
|
|
source1 = (1<<24) - source1;
|
|
}
|
|
if (source2 & (1<<23)) {
|
|
signe ^= 1;
|
|
source2 = (1<<24) - source2;
|
|
}
|
|
|
|
/* bits 0-11 * bits 0-11 */
|
|
part[0]=(source1 & BITMASK(12))*(source2 & BITMASK(12));
|
|
/* bits 12-23 * bits 0-11 */
|
|
part[1]=((source1>>12) & BITMASK(12))*(source2 & BITMASK(12));
|
|
/* bits 0-11 * bits 12-23 */
|
|
part[2]=(source1 & BITMASK(12))*((source2>>12) & BITMASK(12));
|
|
/* bits 12-23 * bits 12-23 */
|
|
part[3]=((source1>>12) & BITMASK(12))*((source2>>12) & BITMASK(12));
|
|
|
|
/* Calc dest 2 */
|
|
dest[2] = part[0];
|
|
dest[2] += (part[1] & BITMASK(12)) << 12;
|
|
dest[2] += (part[2] & BITMASK(12)) << 12;
|
|
|
|
/* Calc dest 1 */
|
|
dest[1] = (part[1]>>12) & BITMASK(12);
|
|
dest[1] += (part[2]>>12) & BITMASK(12);
|
|
dest[1] += part[3];
|
|
|
|
/* Calc dest 0 */
|
|
dest[0] = 0;
|
|
|
|
/* Add carries */
|
|
value = (dest[2]>>24) & BITMASK(8);
|
|
if (value) {
|
|
dest[1] += value;
|
|
dest[2] &= BITMASK(24);
|
|
}
|
|
value = (dest[1]>>24) & BITMASK(8);
|
|
if (value) {
|
|
dest[0] += value;
|
|
dest[1] &= BITMASK(24);
|
|
}
|
|
|
|
/* Get rid of extra sign bit */
|
|
dsp_asl56(dest, 1);
|
|
|
|
if (signe) {
|
|
zerodest[0] = zerodest[1] = zerodest[2] = 0;
|
|
|
|
dsp_sub56(dest, zerodest);
|
|
|
|
dest[0] = zerodest[0];
|
|
dest[1] = zerodest[1];
|
|
dest[2] = zerodest[2];
|
|
}
|
|
}
|
|
|
|
static void dsp_rnd56(dsp_core_t* dsp, uint32_t *dest)
|
|
{
|
|
uint32_t rnd_const[3];
|
|
|
|
rnd_const[0] = 0;
|
|
|
|
/* Scaling mode S0 */
|
|
if (dsp->registers[DSP_REG_SR] & (1<<DSP_SR_S0)) {
|
|
rnd_const[1] = 1;
|
|
rnd_const[2] = 0;
|
|
dsp_add56(rnd_const, dest);
|
|
|
|
if ((dest[2]==0) && ((dest[1] & 1) == 0)) {
|
|
dest[1] &= (0xffffff - 0x3);
|
|
}
|
|
dest[1] &= 0xfffffe;
|
|
dest[2]=0;
|
|
}
|
|
/* Scaling mode S1 */
|
|
else if (dsp->registers[DSP_REG_SR] & (1<<DSP_SR_S1)) {
|
|
rnd_const[1] = 0;
|
|
rnd_const[2] = (1<<22);
|
|
dsp_add56(rnd_const, dest);
|
|
|
|
if ((dest[2] & 0x7fffff) == 0){
|
|
dest[2] = 0;
|
|
}
|
|
dest[2] &= 0x800000;
|
|
}
|
|
/* No Scaling */
|
|
else {
|
|
rnd_const[1] = 0;
|
|
rnd_const[2] = (1<<23);
|
|
dsp_add56(rnd_const, dest);
|
|
|
|
if (dest[2] == 0) {
|
|
dest[1] &= 0xfffffe;
|
|
}
|
|
dest[2]=0;
|
|
}
|
|
}
|
|
|
|
static uint32_t dsp_signextend(int bits, uint32_t v) {
|
|
const int shift = sizeof(int)*8 - bits;
|
|
assert(shift > 0);
|
|
return (uint32_t)(((int32_t)v << shift) >> shift);
|
|
}
|
|
|
|
|