// license:MAME // copyright-holders:Raphael Nabet /************************************************************************ ST register functions ************************************************************************/ /* remember that the OP ST bit is maintained in cpustate->lastparity */ /* setstat sets the ST_OP bit according to cpustate->lastparity It must be called before reading the ST register. */ static void setstat(tms99xx_state *cpustate) { int i; UINT8 a; cpustate->STATUS &= ~ ST_OP; /* We set the parity bit. */ a = cpustate->lastparity; for (i=0; i<8; i++) /* 8 bits to test */ { if (a & 1) /* If current bit is set */ cpustate->STATUS ^= ST_OP; /* we toggle the ST_OP bit */ a >>= 1; /* Next bit. */ } } /* getstat sets emulator's cpustate->lastparity variable according to 9900's STATUS bits. It must be called on interrupt return, or when, for some reason, the emulated program sets the STATUS register directly. */ static void getstat(tms99xx_state *cpustate) { #if (USE_ST_MASK) cpustate->STATUS &= ST_MASK; /* unused bits are forced to 0 */ #endif if (cpustate->STATUS & ST_OP) cpustate->lastparity = 1; else cpustate->lastparity = 0; #if HAS_MAPPING cpustate->cur_map = (cpustate->STATUS & ST_MF) ? 1 : 0; #endif } /* A few words about the following functions. A big portability issue is the behavior of the ">>" instruction with the sign bit, which has not been normalised. Every compiler does whatever it thinks smartest. My code assumed that when shifting right signed numbers, the operand is left-filled with a copy of sign bit, and that when shifting unsigned variables, it is left-filled with 0s. This is probably the most logical behaviour, and it is the behavior of CW PRO3 - most time (the exception is that ">>=" instructions always copy the sign bit (!)). But some compilers are bound to disagree. So, I had to create special functions with predefined tables included, so that this code work on every compiler. BUT this is a real slow-down. So, you might have to include a few lines in assembly to make this work better. Sorry about this, this problem is really unpleasant and absurd, but it is not my fault. */ static const UINT16 right_shift_mask_table[17] = { 0xFFFF, 0x7FFF, 0x3FFF, 0x1FFF, 0x0FFF, 0x07FF, 0x03FF, 0x01FF, 0x00FF, 0x007F, 0x003F, 0x001F, 0x000F, 0x0007, 0x0003, 0x0001, 0x0000 }; static const UINT16 inverted_right_shift_mask_table[17] = { 0x0000, 0x8000, 0xC000, 0xE000, 0xF000, 0xF800, 0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0, 0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF }; INLINE UINT16 logical_right_shift(UINT16 val, int c) { return((val>>c) & right_shift_mask_table[c]); } INLINE INT16 arithmetic_right_shift(INT16 val, int c) { if (val < 0) return((val>>c) | inverted_right_shift_mask_table[c]); else return((val>>c) & right_shift_mask_table[c]); } /* Set lae */ INLINE void setst_lae(tms99xx_state *cpustate, INT16 val) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ); if (val > 0) cpustate->STATUS |= (ST_LGT | ST_AGT); else if (val < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; } /* Set laep (BYTE) */ INLINE void setst_byte_laep(tms99xx_state *cpustate, INT8 val) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ); if (val > 0) cpustate->STATUS |= (ST_LGT | ST_AGT); else if (val < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; cpustate->lastparity = val; } /* For COC, CZC, and TB */ INLINE void setst_e(tms99xx_state *cpustate, UINT16 val, UINT16 to) { if (val == to) cpustate->STATUS |= ST_EQ; else cpustate->STATUS &= ~ ST_EQ; } /* For CI, C, CB */ INLINE void setst_c_lae(tms99xx_state *cpustate, UINT16 to, UINT16 val) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ); if (val == to) cpustate->STATUS |= ST_EQ; else { if ( ((INT16) val) > ((INT16) to) ) cpustate->STATUS |= ST_AGT; if ( ((UINT16) val) > ((UINT16) to) ) cpustate->STATUS |= ST_LGT; } } /* Set laeco for add */ INLINE INT16 setst_add_laeco(tms99xx_state *cpustate, int a, int b) { UINT32 res; INT16 res2; cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV); res = (a & 0xffff) + (b & 0xffff); if (res & 0x10000) cpustate->STATUS |= ST_C; if ((res ^ b) & (res ^ a) & 0x8000) cpustate->STATUS |= ST_OV; #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) if (((a & b) | ((a | b) & ~ res)) & 0x0800) cpustate->STATUS |= ST_DC; #endif res2 = (INT16) res; if (res2 > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (res2 < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; return res2; } /* Set laeco for subtract */ INLINE INT16 setst_sub_laeco(tms99xx_state *cpustate, int a, int b) { UINT32 res; INT16 res2; cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV); res = (a & 0xffff) - (b & 0xffff); if (! (res & 0x10000)) cpustate->STATUS |= ST_C; if ((a ^ b) & (a ^ res) & 0x8000) cpustate->STATUS |= ST_OV; #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) if (((a & ~ b) | ((a | ~ b) & ~ res)) & 0x0800) cpustate->STATUS |= ST_DC; #endif res2 = (INT16) res; if (res2 > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (res2 < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; return res2; } /* Set laecop for add (BYTE) */ INLINE INT8 setst_addbyte_laecop(tms99xx_state *cpustate, int a, int b) { unsigned int res; INT8 res2; cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV | ST_OP); res = (a & 0xff) + (b & 0xff); if (res & 0x100) cpustate->STATUS |= ST_C; if ((res ^ b) & (res ^ a) & 0x80) cpustate->STATUS |= ST_OV; #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) if (((a & b) | ((a | b) & ~ res)) & 0x08) cpustate->STATUS |= ST_DC; #endif res2 = (INT8) res; if (res2 > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (res2 < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; cpustate->lastparity = res2; return res2; } /* Set laecop for subtract (BYTE) */ INLINE INT8 setst_subbyte_laecop(tms99xx_state *cpustate, int a, int b) { unsigned int res; INT8 res2; cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV | ST_OP); res = (a & 0xff) - (b & 0xff); if (! (res & 0x100)) cpustate->STATUS |= ST_C; if ((a ^ b) & (a ^ res) & 0x80) cpustate->STATUS |= ST_OV; #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) if (((a & ~ b) | ((a | ~ b) & ~ res)) & 0x08) cpustate->STATUS |= ST_DC; #endif res2 = (INT8) res; if (res2 > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (res2 < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; cpustate->lastparity = res2; return res2; } /* For NEG */ INLINE void setst_laeo(tms99xx_state *cpustate, INT16 val) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_OV); if (val > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (val < 0) { cpustate->STATUS |= ST_LGT; if (((UINT16) val) == 0x8000) cpustate->STATUS |= ST_OV; } else cpustate->STATUS |= ST_EQ; } /* Meat of SRA */ INLINE UINT16 setst_sra_laec(tms99xx_state *cpustate, INT16 a, UINT16 c) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C); if (c != 0) { a = arithmetic_right_shift(a, c-1); if (a & 1) // The carry bit equals the last bit that is shifted out cpustate->STATUS |= ST_C; a = arithmetic_right_shift(a, 1); } if (a > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (a < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; return a; } /* Meat of SRL. Same algorithm as SRA, except that we fills in with 0s. */ INLINE UINT16 setst_srl_laec(tms99xx_state *cpustate, UINT16 a,UINT16 c) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C); if (c != 0) { a = logical_right_shift(a, c-1); if (a & 1) cpustate->STATUS |= ST_C; a = logical_right_shift(a, 1); } if (((INT16) a) > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (((INT16) a) < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; return a; } // // Meat of SRC // INLINE UINT16 setst_src_laec(tms99xx_state *cpustate, UINT16 a,UINT16 c) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C); if (c != 0) { a = logical_right_shift(a, c) | (a << (16-c)); if (a & 0x8000) // The carry bit equals the last bit that is shifted out cpustate->STATUS |= ST_C; } if (((INT16) a) > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (((INT16) a) < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; return a; } // // Meat of SLA // INLINE UINT16 setst_sla_laeco(tms99xx_state *cpustate, UINT16 a, UINT16 c) { cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV); if (c != 0) { { register UINT16 mask; register UINT16 ousted_bits; mask = 0xFFFF << (16-c-1); ousted_bits = a & mask; if (ousted_bits) // If ousted_bits is neither all 0s if (ousted_bits ^ mask) // nor all 1s, cpustate->STATUS |= ST_OV; // we set overflow } a <<= c-1; if (a & 0x8000) // The carry bit equals the last bit that is shifted out cpustate->STATUS |= ST_C; a <<= 1; } if (((INT16) a) > 0) cpustate->STATUS |= ST_LGT | ST_AGT; else if (((INT16) a) < 0) cpustate->STATUS |= ST_LGT; else cpustate->STATUS |= ST_EQ; return a; } /***********************************************************************/