X-Git-Url: http://shamusworld.gotdns.org/cgi-bin/gitweb.cgi?p=rmac;a=blobdiff_plain;f=dsp56k_mach.c;fp=dsp56k_mach.c;h=20fc07af96eec0f16be143d74d5643c339ba3880;hp=0000000000000000000000000000000000000000;hb=30a208654896284b50e7b362e97d3e63ec717b96;hpb=790cd95291aa2dcfd3128652ef9c34bab1398a8d diff --git a/dsp56k_mach.c b/dsp56k_mach.c new file mode 100644 index 0000000..20fc07a --- /dev/null +++ b/dsp56k_mach.c @@ -0,0 +1,1460 @@ +// +// RMAC - Reboot's Macro Assembler for the Atari Jaguar Console System +// DSP56L_MACH.C - Code Generation for Motorola DSP56001 +// Copyright (C) 199x Landon Dyer, 2011-2018 Reboot and Friends +// RMAC derived from MADMAC v1.07 Written by Landon Dyer, 1986 +// Source utilised with the kind permission of Landon Dyer +// + +#include "dsp56k_mach.h" +#include "direct.h" +#include "dsp56k.h" +#include "error.h" +#include "rmac.h" +#include "sect.h" +#include "token.h" + +#define DEF_KW +#include "kwtab.h" + + +// Globals +unsigned int dsp_orgaddr; // DSP 56001 ORG address +unsigned int dsp_orgseg; // DSP 56001 ORG segment + + +// Fucntion prototypes +int m_unimp(WORD, WORD), m_badmode(WORD, WORD); +int dsp_ab(LONG); +int dsp_baab(LONG inst); +int dsp_acc48(LONG inst); +int dsp_self(LONG inst); +int dsp_xyab(LONG inst); +int dsp_x0y0ab(LONG inst); +int dsp_immcr(LONG inst); +int dsp_immmovec(LONG inst); +int dsp_imm12(LONG inst); +int dsp_tcc2(LONG inst); +int dsp_tcc4(LONG inst); +int dsp_ea(LONG inst); +int dsp_ea_imm5(LONG inst); +int dsp_abs12(LONG inst); +int dsp_reg_imm5(LONG inst); +int dsp_ea_abs16(LONG inst); +int dsp_reg_abs16(LONG inst); +int dsp_imm12_abs16(LONG inst); +int dsp_alu24_abs16(LONG inst); +int dsp_reg(LONG inst); +int dsp_alu24(LONG inst); +int dsp_reg_imm5_abs16(LONG inst); +int dsp_ea_imm5_abs16(LONG inst); +int dsp_ea_lua(LONG inst); +int dsp_ab_rn(LONG inst); +int dsp_movec_ea(LONG inst); +int dsp_movec_aa(LONG inst); +int dsp_movec_reg(LONG inst); +int dsp_mult(LONG inst); +int dsp_movem_ea(LONG inst); +int dsp_movem_aa(LONG inst); +int dsp_movep_ea(LONG inst); +int dsp_movep_reg(LONG inst); + + +// Common error messages + + +// Include code tables +MNTABDSP dsp56k_machtab[] = { + { 0xFFFFFFFF, 0xFFFFFFFF, 0x0000, 0x0000, 0, (int (*)(LONG))m_badmode }, // 0 + #include "dsp56ktab.h" + { 0L, 0L, 0x0000, 0x0000, 0, (int (*)(LONG))m_unimp } // Last entry +}; + + +static inline int dsp_extra_ea() +{ + if (deposit_extra_ea == DEPOSIT_EXTRA_WORD) + { + if (!(dspImmedEXATTR&FLOAT)) + { + if (dspImmedEXATTR & DEFINED) + { + D_dsp(dspImmedEXVAL); + } + else + { + // TODO: what if it's an address and not an immediate? Does it matter at all? + AddFixup(FU_DSPIMM24, sloc, dspImmedEXPR); + D_dsp(0); + } + } + else + { + if (dspImmedEXATTR & DEFINED) + { + D_dsp(dspImmedEXVAL); + } + else + { + // TODO: what if it's an address and not an immediate? Does it matter at all? + AddFixup(FU_DSPIMMFL24, sloc, dspImmedEXPR); + D_dsp(0); + } + } + } + else if (deposit_extra_ea == DEPOSIT_EXTRA_FIXUP) + { + // Probably superfluous check (we're not likely to land here with a + // known aa) but oh well + if (!(dspImmedEXATTR & DEFINED)) + { + // Since we already deposited the word to be fixup'd we need to + // subtract 1 from sloc + chptr -= 3; + AddFixup(FU_DSPADR06, sloc - 1, dspImmedEXPR); + chptr += 3; + } + } + + return OK; +} + + +int dsp_ab(LONG inst) +{ + inst |= (dsp_a0reg & 1) << 3; + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + return OK; +} + + +int dsp_baab(LONG inst) +{ + if (dsp_a0reg == dsp_a1reg) + return error("source and destination registers must not be the same"); + + inst |= ((dsp_a0reg + 1) & 1) << 3; + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + + return OK; +} + + +int dsp_acc48(LONG inst) +{ + if (dsp_a0reg == dsp_a1reg) + return error("source and destination registers must not be the same"); + + inst |= (dsp_a1reg & 1) << 3; + + switch (dsp_a0reg) + { + case KW_X: inst |= 2 << 4; break; + case KW_Y: inst |= 3 << 4; break; + case KW_X0: inst |= 4 << 4;break; + case KW_Y0: inst |= 5 << 4;break; + case KW_X1: inst |= 6 << 4;break; + case KW_Y1: inst |= 7 << 4;break; + default: return error("dsp_acc48: shouldn't reach here!"); + } + + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + + return OK; +} + + +int dsp_self(LONG inst) +{ + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + + return OK; +} + + +int dsp_xyab(LONG inst) +{ + if (dsp_a0reg == dsp_a1reg) + return error("source and destination registers must not be the same"); + + inst |= (dsp_a0reg & 1) << 4; + inst |= (dsp_a1reg & 1) << 3; + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + + return OK; +} + + +int dsp_x0y0ab(LONG inst) +{ + if (dsp_a0reg == dsp_a1reg) + return error("source and destination registers must not be the same"); + + int inverse = (dsp_a0reg & 3); + inverse = ((inverse & 1) << 1) | ((inverse & 2) >> 1); + inst |= inverse << 4; + inst |= (dsp_a1reg & 1) << 3; + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + + return OK; +} + + +int dsp_immcr(LONG inst) +{ + switch (dsp_a1reg) + { + case KW_CCR: inst |= 1; break; + case KW_MR:inst |= 0; break; + case KW_OMR:inst |= 2; break; + default: return error("invalid destination register (only ccr, mr, omr allowed"); + } + + if (dsp_a0exattr & DEFINED) + { + inst |= dsp_a0exval << 8; + D_dsp(inst); + } + else + { + AddFixup(FU_DSPIMM8, sloc, dsp_a0expr); + D_dsp(inst); + } + + return OK; +} + + +int dsp_immmovec(LONG inst) +{ + switch (dsp_a1reg) + { + case 0: + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + case 7: inst |= dsp_a1reg; break; // M0-M7 + case KW_SR: inst |= 25; break; + case KW_OMR: inst |= 26; break; + case KW_SP: inst |= 27; break; + case KW_SSH: inst |= 28; break; + case KW_SSL: inst |= 29; break; + case KW_LA: inst |= 30; break; + case KW_LC: inst |= 31; break; + default: return error("invalid destination register (only m0-m7, SR, OMR, SP, SSH, SSL, LA, LC allowed"); + } + + if (dsp_a0exattr & DEFINED) + { + inst |= (dsp_a0exval & 0xff) << 8; + D_dsp(inst); + } + else + { + AddFixup(FU_DSPIMM8, sloc, dsp_a0expr); + D_dsp(inst); + } + + return OK; +} + + +int dsp_imm12(LONG inst) +{ + if (dsp_a0exattr & DEFINED) + { + if ((dsp_am0 & (M_DSPIM12 | M_DSPIM8)) == 0) + return error("immediate out of range ($000-$fff)"); + inst |= ((dsp_a0exval & 0x0ff) << 8) | ((dsp_a0exval & 0xf00) >> 8); + D_dsp(inst); + } + else + { + AddFixup(FU_DSPIMM12, sloc, dsp_a0expr); + D_dsp(inst); + } + + return OK; +} + + +// Tcc instructions with 2 operands (48bit) +int dsp_tcc2(LONG inst) +{ + if (dsp_a0reg == dsp_a1reg) + return error("First pair of source and destination registers must not be the same"); + + int inverse; + inverse = (dsp_a0reg & 7); + inverse = ((inverse & 1) << 1) | ((inverse & 2) >> 1) | (inverse & 4); + inst |= inverse << 4; + inst |= ((dsp_a1reg) & 1) << 3; + D_dsp(inst); + + return OK; +} + + +// Tcc instructions with 4 operands +int dsp_tcc4(LONG inst) +{ + if (dsp_a0reg == dsp_a1reg) + return error("First pair of source and destination registers must not be the same"); + + if (dsp_am2 != M_DSPR || dsp_am3 != M_DSPR) + return error("Second pair of source and destination registers must be R0-R7"); + + if (dsp_am0 == M_ACC56 && dsp_am1 == M_ACC56) + { + inst |= ((dsp_a0reg + 1) & 1) << 3; + } + else + { + int inverse; + inverse = (dsp_a0reg & 7); + inverse = ((inverse & 1) << 1) | ((inverse & 2) >> 1) | (inverse & 4); + inst |= inverse << 4; + inst |= ((dsp_a1reg) & 1) << 3; + } + + inst |= 1 << 16; + inst |= (dsp_a2reg << 8) | (dsp_a3reg); + D_dsp(inst); + + return OK; +} + + +// Just store ea +int dsp_ea(LONG inst) +{ + inst |= dsp_a0reg << 8; + + if (dsp_a0memspace != -1) + inst |= dsp_a0memspace; + + if (dsp_am0 == M_DSPAA) + inst |= ((dsp_a0exval & 0x3f) << 8); + + D_dsp(inst); + + if (dsp_a0reg == DSP_EA_ABS) + { + if (dsp_a0exattr & DEFINED) + { + D_dsp(dsp_a0exval); + } + else + { + AddFixup(FU_DSPADR24, sloc, dsp_a0expr); + D_dsp(0); + } + } + + return OK; +} + + +// Store ea and 5-bit constant +int dsp_ea_imm5(LONG inst) +{ + if (dsp_a1memspace == -1) + return error("Only X: or Y: memory space allowed"); + + if (dsp_a0exattr & DEFINED) + { + int v = (int)dsp_a0exval; + + if (v < 0 || v > 23) + return error("immediate value must be between 0 and 23"); + + if (dsp_a1reg == DSP_EA_ABS) + { + inst |= (v | dsp_a1memspace | (dsp_a1reg << 8)); + } + else + { + inst |= ((dsp_a1exval & 0x3F) << 8) | v | dsp_a1memspace | (dsp_a1reg << 8); + } + + D_dsp(inst); + + if (dsp_a1reg == DSP_EA_ABS) + { + if (dsp_a1exattr & DEFINED) + { + D_dsp(dsp_a1exval); + } + else + { + AddFixup(FU_DSPADR24, sloc, dsp_a1expr); + D_dsp(0); + } + } + } + else + { + if (dsp_a1reg == DSP_EA_ABS) + { + inst |= dsp_a1memspace | (dsp_a1reg << 8); + } + else + { + inst |= ((dsp_a1exval & 0x3F) << 8) | dsp_a1memspace | (dsp_a1reg << 8); + } + + AddFixup(FU_DSPIMM5, sloc, dsp_a0expr); + D_dsp(inst); + + if (dsp_a1reg == DSP_EA_ABS) + { + if (dsp_a1exattr & DEFINED) + { + D_dsp(dsp_a1exval); + } + else + { + AddFixup(FU_DSPADR24, sloc, dsp_a1expr); + D_dsp(0); + } + } + } + + return OK; +} + + +// Processes the input register according to table A-18 of the Motorola DSP +// manual and returns the correct encoding. +// Note: returns only the 3 lower bits of the table. The rest is handled in +// dsp56ktab. +static inline LONG tab_A18(int *am, int *reg) +{ + switch (*am) + { + case M_ALU24: + return (*reg & 7); + case M_DSPM: + case M_DSPN: + case M_DSPR: + return *reg; + break; + case M_ACC56: + case M_ACC24: + case M_ACC8: + if (*reg == KW_A1) + return 4; + else + return (*reg & 7); + + break; + case M_DSPPCU: + switch (*reg) + { + case KW_SR: return 1; break; + case KW_OMR: return 2; break; + case KW_SP: return 3; break; + case KW_SSH: return 4; break; + case KW_SSL: return 5; break; + case KW_LA: return 6; break; + case KW_LC: return 7; break; + default: + return error("specified control register not allowed as destination"); + break; + } + + break; + default: + return error("reached at the end of tab_A18 - shouldn't happen!"); + } +} + + +// Store register (table A-18 in the motorola manual) and 5-bit constant +int dsp_reg_imm5(LONG inst) +{ + LONG reg; + + if ((reg = tab_A18(&dsp_am1, &dsp_a1reg)) == ERROR) + return ERROR; + + inst |= (reg << 8); + + if (dsp_a0exattr & DEFINED) + { + int v = (int)dsp_a0exval; + + if (v < 0 || v > 23) + return error("immediate value must be between 0 and 23"); + + inst |= v; + D_dsp(inst); + } + else + { + AddFixup(FU_DSPIMM5, sloc, dsp_a0expr); + D_dsp(inst); + } + + return OK; +} + + +// Store 12-bit address +int dsp_abs12(LONG inst) +{ + if (dsp_a0exattr & DEFINED) + { + int v = (int)dsp_a0exval; + + if (v < 0 || v > 0xFFF) + return error("immediate out of range ($000-$FFF)"); + + inst |= v; + D_dsp(inst); + } + else + { + AddFixup(FU_DSPADR12, sloc, dsp_a0expr); + D_dsp(inst); + } + + return OK; +} + + +// Manipulate expr to append a '-1'. Used specifically for DO. +void append_minus_1(TOKEN * expr) +{ + // Find where the end of expression is + while (*expr != ENDEXPR) + { + if (*expr == SYMBOL || *expr == CONST || *expr == FCONST) + expr++; + else if (*expr == ACONST) + expr += 3; + + expr++; + } + + // Overwrite ENDEXPR and append '-1' + *expr++ = CONST; + uint64_t *expr64 = (uint64_t *)expr; + *expr64++ = 1; + expr = (uint32_t *)expr64; + *expr++ = '-'; + *expr = ENDEXPR; +} + + +// Store a 12bit immediate and 16bit address. +// Note: This function is specifically handling DO. DO has a requirement of +// storing the address of a label minus 1! Quoting the manual: +// "Note: The assembler calculates the end-of-loop address to be loaded +// into LA (the absolute address extension word) by evaluating the end +// -of-loop expression and subtracting one. This is done to +// accommodate the case where the last word in the DO loop is a two-word +// instruction. Thus, the end-of-loop expression in the source +// code must represent the address of the instruction AFTER the last +// instruction in the loop as shown in the example." +// This is fine if we know the address already, but a problem when we +// don't. +int dsp_imm12_abs16(LONG inst) +{ + if (dsp_a0exattr & DEFINED) + { + if ((dsp_am0 & (M_DSPIM12 | M_DSPIM8)) == 0) + return error("immediate out of range ($000-$FFF)"); + + inst |= ((dsp_a0exval & 0x0FF) << 8) | ((dsp_a0exval & 0xF00) >> 8); + D_dsp(inst); + } + else + { + AddFixup(FU_DSPIMM12, sloc, dsp_a0expr); + D_dsp(inst); + } + + if (dsp_a1exattr & DEFINED) + { + D_dsp((a1exval - 1)); + } + else + { + append_minus_1(dsp_a1expr); + AddFixup(FU_DSPADR16, sloc, dsp_a1expr); + D_dsp(0); + } + + return OK; +} + + +// Just store ea and 16bit address +// Note: this function is specifically handling DO. +// The same notes as dsp_imm12_abs16 apply here. +int dsp_ea_abs16(LONG inst) +{ + if ((dsp_a0reg == DSP_EA_ABS && dsp_a0memspace == -1) || dsp_a1reg == DSP_EA_IMM) + return error("immediate values > 31 or absolute values not allowed"); + + if (dsp_a0exattr & DEFINED) + { + if (dsp_a0exval > 31) + return error("absolute address (aa) bigger than $1F"); + + inst |= dsp_a0exval << 8; + } + + inst |= dsp_a0reg << 8; + + if (dsp_a0memspace == -1) + return error("only X:, Y: address spaces allowed"); + + if ((deposit_extra_ea == DEPOSIT_EXTRA_FIXUP) || (dsp_a0reg == DSP_EA_ABS && dsp_am0 == M_DSPEA)) + { + // Change instruction to aa instead of ea. TODO: check if this is true + // for all cases + inst = 0x060000; + inst |= dsp_a0memspace; + + // Probably superfluous check (we're not likely to land here with a + // known aa) but oh well + if (!(dsp_a0exattr & DEFINED)) + { + AddFixup(FU_DSPADR06, sloc, dsp_a0expr); + D_dsp(inst); + } + else + { + D_dsp(inst); + } + } + else + { + inst |= dsp_a0memspace; + D_dsp(inst); + } + + if (dsp_a1exattr & DEFINED) + { + D_dsp((dsp_a1exval - 1)); + } + else + { + append_minus_1(dsp_a1expr); + AddFixup(FU_DSPADR16, sloc, dsp_a1expr); + D_dsp(0); + } + + return OK; +} + + +// Store register (table A-18 in the motorola manual) 5-bit constant and 16bit address +// Note: this function is specifically handling DO. +// The same notes as dsp_imm12_abs16 apply here. +int dsp_reg_abs16(LONG inst) +{ + LONG reg; + + if ((reg = tab_A18(&dsp_am0, &dsp_a0reg)) == ERROR) + return ERROR; + + inst |= reg << 8; + + if (dsp_a1exattr & DEFINED) + { + int v = (int)dsp_a1exval - 1; + D_dsp(inst); + D_dsp(v); + } + else + { + D_dsp(inst); + append_minus_1(dsp_a1expr); + AddFixup(FU_DSPADR16, sloc, dsp_a1expr); + D_dsp(0); + } + + return OK; +} + + +// Store ALU24 register and 16bit address +// Note: this function is specifically handling DO. +// The same notes as dsp_imm12_abs16 apply here. +int dsp_alu24_abs16(LONG inst) +{ + inst |= (dsp_a0reg & 7) << 8; + + if (dsp_a1exattr & DEFINED) + { + int v = (int)dsp_a1exval - 1; + D_dsp(inst); + D_dsp(v); + } + else + { + D_dsp(inst); + append_minus_1(dsp_a1expr); + AddFixup(FU_DSPADR16, sloc, dsp_a1expr); + D_dsp(0); + } + + return OK; +} + + +// Store register (table A-18 in the motorola manual) +int dsp_reg(LONG inst) +{ + LONG reg; + + if ((reg = tab_A18(&dsp_am0, &dsp_a0reg)) == ERROR) + return ERROR; + + inst |= reg << 8; + D_dsp(inst); + + return OK; +} + + +int dsp_alu24(LONG inst) +{ + inst |= (dsp_a0reg & 7) << 8; + D_dsp(inst); + + return OK; +} + + +// Store register (table A-18 in the motorola manual) and 5-bit constant +int dsp_reg_imm5_abs16(LONG inst) +{ + LONG reg; + + // First, check that we have at best an 16bit absolute address in + // operand 3 since we don't check that anywhere else + if (dsp_a2exattr & DEFINED) + { + if ((dsp_am2 & C_DSPABS16) == 0) + return error("expected 16-bit address as third operand."); + } + + if ((reg = tab_A18(&dsp_am1, &dsp_a1reg)) == ERROR) + return ERROR; + + inst |= reg << 8; + + if (dsp_a0exattr & DEFINED) + { + int v = (int)dsp_a0exval; + + if (v < 0 || v > 23) + return error("immediate value must be between 0 and 23"); + + inst |= v; + D_dsp(inst); + + if (dsp_a2exattr & DEFINED) + { + int v = (int)dsp_a2exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR16, sloc, dsp_a2expr); + D_dsp(0); + } + } + else + { + AddFixup(FU_DSPIMM5, sloc, dsp_a0expr); + D_dsp(inst); + + if (dsp_a2exattr & DEFINED) + { + int v = (int)dsp_a2exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR16, sloc, dsp_a2expr); + D_dsp(0); + } + } + + return OK; +} + + +// Store ea, 5-bit constant and 16-bit address in the extension word +int dsp_ea_imm5_abs16(LONG inst) +{ + // First, check that we have at best an 16bit absolute address in + // operand 3 since we don't check that anywhere else + if (dsp_a2exattr&DEFINED) + { + if ((dsp_am2&C_DSPABS16) == 0) + return error("expected 16-bit address as third operand."); + } + + if (dsp_a1memspace == -1) + return error("Only X: or Y: memory space allowed"); + + if (dsp_am1 == M_DSPAA) + { + if (dsp_a1exattr & DEFINED) + inst |= (dsp_a1exval & 0x3F) << 8; + else + AddFixup(FU_DSPADR06, sloc, dsp_a1expr); + } + + if (dsp_am1 == M_DSPPP) + { + if (dsp_a1exattr & DEFINED) + inst |= (dsp_a1exval & 0x3f) << 8; + else + AddFixup(FU_DSPPP06, sloc, dsp_a1expr); + } + + if (dsp_a0exattr & DEFINED) + { + int v = (int)dsp_a0exval; + + if (v < 0 || v > 23) + return error("immediate value must be between 0 and 23"); + + inst |= (dsp_a1reg << 8) | v | dsp_a1memspace; + D_dsp(inst); + + if (dsp_a2exattr & DEFINED) + { + int v = (int)dsp_a2exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR16, sloc, dsp_a2expr); + D_dsp(0); + } + } + else + { + inst |= (dsp_a1reg << 8) | dsp_a1memspace; + AddFixup(FU_DSPIMM5, sloc, dsp_a0expr); + D_dsp(inst); + + if (dsp_a2exattr & DEFINED) + { + int v = (int)dsp_a2exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR16, sloc, dsp_a2expr); + D_dsp(0); + } + } + + return OK; +} + + +int dsp_ea_lua(LONG inst) +{ + int am = dsp_a0reg & 0x38; + + if (am != DSP_EA_POSTDEC && am != DSP_EA_POSTINC && + am != DSP_EA_POSTDEC1 && am != DSP_EA_POSTINC1) + return error("addressing mode not allowed"); + + inst |= dsp_a0reg << 8; + + if (dsp_am1 == M_DSPN) + inst |= 1 << 3; + + inst |= dsp_a1reg; + D_dsp(inst); + + return OK; +} + + +int dsp_ab_rn(LONG inst) +{ + inst |= (dsp_a1reg & 1) << 3; + inst |= (dsp_a0reg) << 8; + D_dsp(inst); + + return OK; +} + + +int dsp_movec_ea(LONG inst) +{ + int ea = dsp_a1reg; + int memspace = dsp_a1memspace; + WORD exattr = dsp_a1exattr; + LONG exval = (uint32_t)dsp_a1exval; + TOKEN * expr = dsp_a1expr; + int reg = dsp_a0reg; + int am = dsp_am0; + int reg2 = dsp_a1reg; + + if (dsp_am0 == M_DSPEA || (dsp_am0 & C_DSPIM)) + { + ea = dsp_a0reg; + exattr = dsp_a0exattr; + exval = (uint32_t)dsp_a0exval; + memspace = dsp_a0memspace; + expr = dsp_a0expr; + reg = dsp_a1reg; + reg2 = dsp_a0reg; + am = dsp_am1; + } + + // Abort if unsupported registers are requested + if (reg == KW_PC || reg == KW_MR || reg == KW_CCR) + return error("illegal registers for instruction."); + + if (dsp_am0 & C_DSPIM) + memspace = 0; + + if (memspace == -1) + return error("only x: or y: memory spaces allowed."); + + // No memspace required when loading an immediate + if (dsp_am0 & C_DSPIM) + memspace = 0; + + reg = tab_A18(&am, ®); + inst |= (ea << 8) | memspace | reg; + + if (am == M_DSPPCU) + inst |= 3 << 3; + + D_dsp(inst); + + if (reg2 == DSP_EA_ABS || (dsp_am0 & C_DSPIM)) + { + if (exattr & DEFINED) + { + int v = exval; + D_dsp(v); + } + else + { + if (dsp_am0 == M_DSPIM) + { + AddFixup(FU_DSPIMM24, sloc, expr); + D_dsp(0); + } + else + { + AddFixup(FU_DSPADR24, sloc, expr); + D_dsp(0); + } + } + } + + return OK; +} + + +int dsp_movec_aa(LONG inst) +{ + int ea = dsp_a1reg; + int memspace = dsp_a1memspace; + WORD exattr = dsp_a1exattr; + LONG exval = (uint32_t)dsp_a1exval; + TOKEN * expr = dsp_a1expr; + int reg = dsp_a0reg; + int am = dsp_am0; + int reg2 = dsp_a1reg; + + if (dsp_am0 == M_DSPAA) + { + ea = dsp_a0reg; + exattr = dsp_a0exattr; + exval = (uint32_t)dsp_a0exval; + memspace = dsp_a0memspace; + expr = dsp_a0expr; + reg = dsp_a1reg; + reg2 = dsp_a0reg; + am = dsp_am1; + } + + // Abort if unsupported registers are requested + if (reg == KW_PC || reg == KW_MR || reg == KW_CCR) + return error("PC, MR, CCR are illegal registers for this instruction."); + + if (memspace == -1) + return error("only x: or y: memory spaces allowed."); + + reg = tab_A18(&am, ®); + inst |= (ea << 8) | memspace | reg; + + if (am == M_DSPPCU) + inst |= 3 << 3; + + if (exattr & DEFINED) + { + inst |= exval << 8; + D_dsp(inst); + } + else + { + AddFixup(FU_DSPADR06, sloc, expr); + D_dsp(inst); + } + + return OK; +} + + +int dsp_movec_reg(LONG inst) +{ + int am0 = dsp_am0; + int am1 = dsp_am1; + + // Abort if unsupported registers are requested + if (dsp_a0reg == KW_PC || dsp_a0reg == KW_MR || dsp_a0reg == KW_CCR || + dsp_a1reg == KW_PC || dsp_a1reg == KW_MR || dsp_a1reg == KW_CCR) + return error("PC, MR, CCR are illegal registers for this instruction."); + + int reg1 = tab_A18(&dsp_am0, &dsp_a0reg); + int reg2 = tab_A18(&dsp_am1, &dsp_a1reg); + + if (inst & (1 << 15)) + { + // S1,D2 + } + else + { + // S2,D1 + int temp = am0; + am0 = am1; + am1 = temp; + temp = reg1; + reg1 = reg2; + reg2 = temp; + } + + switch (am0) + { + case M_ALU24: reg1 |= 0x00; break; + case M_ACC8: + case M_ACC24: reg1 |= 0x08; break; + case M_ACC56: reg1 |= 0x0E; break; + case M_DSPR: reg1 |= 0x10; break; + case M_DSPN: reg1 |= 0x18; break; + case M_DSPM: reg1 |= 0x20; break; + case M_DSPPCU: reg1 |= 0x38; break; + default: + return error("reached the end of dsp_movec_reg case 1 - should not happen!"); + } + + switch (am1) + { + case M_DSPM: reg2 |= 0x00; break; + case M_DSPPCU: reg2 |= 0x18; break; + default: + return error("reached the end of dsp_movec_reg case 2 - should not happen!"); + } + + inst |= (reg1 << 8) | reg2; + D_dsp(inst); + + return OK; +} + + +int dsp_mult(LONG inst) +{ + if (dsp_am2 != M_ACC56) + return error("only A or B allowed as third operand."); + + switch (((dsp_a0reg & 3) << 2) + (dsp_a1reg & 3)) + { + case (0 << 2) + 0: inst |= 0 << 4; break; // x0 x0 + case (2 << 2) + 2: inst |= 1 << 4; break; // y0 y0 + case (1 << 2) + 0: inst |= 2 << 4; break; // x1 x0 + case (0 << 2) + 1: inst |= 2 << 4; break; // x0 x1 + case (3 << 2) + 2: inst |= 3 << 4; break; // y1 y0 + case (2 << 2) + 3: inst |= 3 << 4; break; // y0 y1 + case (0 << 2) + 3: inst |= 4 << 4; break; // x0 y1 + case (3 << 2) + 0: inst |= 4 << 4; break; // y1 x0 + case (2 << 2) + 0: inst |= 5 << 4; break; // y0 x0 + case (0 << 2) + 2: inst |= 5 << 4; break; // x0 y0 + case (1 << 2) + 2: inst |= 6 << 4; break; // x1 y0 + case (2 << 2) + 1: inst |= 6 << 4; break; // y0 x1 + case (3 << 2) + 1: inst |= 7 << 4; break; // y1 x1 + case (1 << 2) + 3: inst |= 7 << 4; break; // x1 y1 + default: + return error("x0/y0/x1/y1 combination not allowed for multiplication."); + } + + if (dsp_a2reg == KW_B) + inst |= 1 << 3; + + inst |= dsp_k; + D_dsp(inst); + dsp_extra_ea(); // Deposit effective address if needed + + return OK; +} + + +int dsp_movem_ea(LONG inst) +{ + int ea = dsp_a1reg; + int memspace = dsp_a0memspace; + WORD exattr = dsp_a1exattr; + LONG exval = (uint32_t)dsp_a1exval; + TOKEN * expr = dsp_a1expr; + int reg = dsp_a0reg; + int am = dsp_am0; + int reg2 = dsp_a1reg; + + if (dsp_am0 == M_DSPEA || dsp_am0 == M_DSPIM) + { + ea = dsp_a0reg; + exattr = dsp_a0exattr; + exval = (uint32_t)dsp_a0exval; + memspace = dsp_a0memspace; + expr = dsp_a0expr; + reg = dsp_a1reg; + reg2 = dsp_a0reg; + am = dsp_am1; + inst |= 1 << 15; + } + + // Abort if unsupported registers are requested + if (reg == KW_PC || reg == KW_MR || reg == KW_CCR) + return error("illegal registers for instruction."); + + if (memspace != -1) + return error("only p: memory space allowed."); + + reg = tab_A18(&am, ®); + inst |= (ea << 8) | reg; + + if (am == M_DSPPCU) + inst |= 3 << 3; + + D_dsp(inst); + + if (reg2 == DSP_EA_ABS || dsp_am0 == M_DSPIM) + { + if (exattr & DEFINED) + { + int v = exval; + D_dsp(v); + } + else + { + if (dsp_am0 == M_DSPIM) + { + AddFixup(FU_DSPIMM24, sloc, expr); + D_dsp(0); + } + else + { + AddFixup(FU_DSPADR24, sloc, expr); + D_dsp(0); + } + } + } + + return OK; +} + + +int dsp_movem_aa(LONG inst) +{ + int ea = dsp_a1reg; + int memspace = dsp_a1memspace; + WORD exattr = dsp_a1exattr; + LONG exval = (uint32_t)dsp_a1exval; + TOKEN * expr = dsp_a1expr; + int reg = dsp_a0reg; + int am = dsp_am0; + int reg2 = dsp_a1reg; + + if (dsp_am0 == M_DSPAA) + { + ea = dsp_a0reg; + exattr = dsp_a0exattr; + exval = (uint32_t)dsp_a0exval; + memspace = dsp_a0memspace; + expr = dsp_a0expr; + reg = dsp_a1reg; + reg2 = dsp_a0reg; + am = dsp_am1; + } + + // Abort if unsupported registers are requested + if (reg == KW_PC || reg == KW_MR || reg == KW_CCR) + return error("PC, MR, CCR are illegal registers for this instruction."); + + if (memspace != -1) + return error("only p: memory space allowed."); + + reg = tab_A18(&am, ®); + inst |= (ea << 8) | reg; + + if (am == M_DSPPCU) + inst |= 3 << 3; + + if (exattr & DEFINED) + { + inst |= exval << 8; + D_dsp(inst); + } + else + { + AddFixup(FU_DSPADR06, sloc, expr); + D_dsp(inst); + } + + return OK; +} + + +int dsp_movep_ea(LONG inst) +{ + // movep doesn't allow any aa modes but we might detect this during amode + // detection. No worries, just change it to ea with extra address instead + if (dsp_am0 == M_DSPAA) + { + dsp_a0reg = DSP_EA_ABS; + dsp_a1reg = 0; + } + if (dsp_am1 == M_DSPAA) + { + dsp_a0reg = 0; + dsp_a1reg = DSP_EA_ABS; + } + + // So we might encounter something like 'movep x:pp,x:pp' which we + // obviously flagged as M_DSPPP during ea parsing. In this case we give up + // and declare the second term as a classic absolute address (we chop off + // the high bits too) and let the routine treat is as such. At least that's + // what Motorola's assembler seems to be doing. + if (dsp_am0 == M_DSPPP && dsp_am1 == M_DSPPP) + { + dsp_am1 = DSP_EA_ABS; + dsp_a1reg = DSP_EA_ABS; + dsp_a1exval &= 0xFFFF; + } + + // Assume first operand is :pp + int ea = dsp_a1reg; + int memspace = dsp_a1memspace; + int perspace = dsp_a0perspace; + WORD exattr = dsp_a1exattr; + WORD exattr2 = dsp_a0exattr; + LONG exval = (uint32_t)dsp_a1exval; + LONG exval2 = (uint32_t)dsp_a0exval; + TOKEN * expr = dsp_a1expr; + TOKEN * expr2 = dsp_a0expr; + int reg = dsp_a0reg; + int am = dsp_am0; + int reg2 = dsp_a1reg; + + if (dsp_am1 == M_DSPPP) + { + ea = dsp_a0reg; + exattr = dsp_a0exattr; + exattr2 = dsp_a1exattr; + exval = (uint32_t)dsp_a0exval; + exval2 = (uint32_t)dsp_a1exval; + memspace = dsp_a0memspace; + perspace = dsp_a1perspace; + expr = dsp_a0expr; + expr2 = dsp_a1expr; + reg = dsp_a1reg; + reg2 = dsp_a0reg; + am = dsp_am1; + } + + if (dsp_a0perspace == -1 && dsp_a1perspace == -1) + { + // Ok, so now we have to guess which of the two parameters is X:pp or + // Y:pp. This happened because we didn't get a << marker in any of the + // addressing modes + if (((dsp_a0exattr | dsp_a1exattr) & DEFINED) == 0) + // You have got to be shitting me... + // One way to deal with this (for example X:ea,X:pp / X:pp,X:ea + // aliasing would be to check number ranges and see which one is + // negative. ...unless one of the two isn't known during this phase + return error("internal assembler error: could not deduce movep syntax"); + + if (dsp_a0exattr & DEFINED) + { + if (dsp_a0exval >= 0xFFC0 && dsp_a0exval <= 0xFFFF) + { + // First operand is :pp - do nothing + perspace = dsp_a0memspace << 10; + // When the source contains a << then we bolt on the :pp + // address during ea parsing, but since we couldn't recognise + // the addressing mode in this case let's just insert it right + // now... + reg |= (dsp_a0exval & 0x3F); + } + } + + if (dsp_a1exattr & DEFINED) + { + if (dsp_a1exval >= 0xFFC0 && dsp_a1exval <= 0xFFFF) + { + ea = dsp_a0reg; + exattr = dsp_a0exattr; + exval = (uint32_t)dsp_a0exval; + memspace = dsp_a0memspace; + perspace = dsp_a1memspace << 10; + expr = dsp_a0expr; + reg = dsp_a0reg; + reg2 = dsp_a1reg; + am = dsp_am1; + // See above + reg |= (dsp_a1exval & 0x3F); + } + } + + if (perspace == -1) + // You have got to be shitting me (twice)... + return error("internal assembler error: could not deduce movep syntax"); + } + + inst |= reg | (ea << 8) | perspace; // reg contains memory space + + if ((dsp_am0 & (M_DSPIM | M_DSPIM8 | M_DSPIM12)) == 0) + { + if (memspace == -1) + { + inst &= ~(1 << 7); + inst |= 1 << 6; + } + else + inst |= memspace; + } + + if (am == M_DSPPP) + { + if (exattr2&DEFINED) + { + inst |= (exval2 & 0x3F); + D_dsp(inst); + } + else + { + AddFixup(FU_DSPIMM5, sloc, expr2); + D_dsp(inst); + } + } + else + { + D_dsp(inst); + } + + if (dsp_am0 & (M_DSPIM | M_DSPIM8 | M_DSPIM12)) + { + if (dsp_a0exattr & DEFINED) + { + int v = (int)dsp_a0exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR16, sloc, dsp_a0expr); + D_dsp(0); + } + } + else if (reg2 == DSP_EA_ABS) + { + if (exattr & DEFINED) + { + int v = exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR24, sloc, expr); + D_dsp(0); + } + } + + return OK; +} + + +int dsp_movep_reg(LONG inst) +{ + // Assume first operand is :pp + int ea = dsp_a0reg; + int memspace = dsp_a0memspace; + int perspace = dsp_a0perspace; + WORD exattr = dsp_a1exattr; + LONG exval = (uint32_t)dsp_a1exval; + TOKEN * expr = dsp_a1expr; + int reg = dsp_a0reg; + int am = dsp_am1; + int reg2 = dsp_a1reg; + + if (dsp_am1 == M_DSPPP) + { + ea = dsp_a1reg; + exattr = dsp_a0exattr; + exval = (uint32_t)dsp_a0exval; + memspace = dsp_a1memspace; + perspace = dsp_a1perspace; + expr = dsp_a0expr; + reg = dsp_a1reg; + reg2 = dsp_a0reg; + am = dsp_am0; + } + + // Abort if unsupported registers are requested + if (reg == KW_PC || reg == KW_MR || reg == KW_CCR) + return error("illegal registers for instruction."); + + reg2 = tab_A18(&am, ®2); + inst |= (reg2 << 8) | reg; + + if (perspace == -1) + return error("only x: or y: memory space allowed."); + else + inst |= perspace; + + D_dsp(inst); + + if (reg2 == DSP_EA_ABS) + { + if (exattr & DEFINED) + { + int v = exval; + D_dsp(v); + } + else + { + AddFixup(FU_DSPADR24, sloc, expr); + D_dsp(0); + } + } + + return OK; +} +