diff options
author | 2015-09-13 08:41:44 +0200 | |
---|---|---|
committer | 2015-09-13 08:41:44 +0200 | |
commit | f88cefad27a1737c76e09d99c9fb43e173506081 (patch) | |
tree | 2d8167d03579c46e226471747eb4407bd00ed6fa /src/emu/cpu/tms9900 | |
parent | e92ac9e0fa8e99869894bea00589bbb526be30aa (diff) |
Move all devices into separate part of src tree (nw)
Diffstat (limited to 'src/emu/cpu/tms9900')
-rw-r--r-- | src/emu/cpu/tms9900/9900dasm.c | 812 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/99xxcore.h | 5319 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/ti990_10.c | 148 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/ti990_10.h | 61 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms9900.c | 2736 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms9900.h | 403 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms9980a.c | 300 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms9980a.h | 56 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms9995.c | 3466 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms9995.h | 446 | ||||
-rw-r--r-- | src/emu/cpu/tms9900/tms99com.h | 93 |
11 files changed, 0 insertions, 13840 deletions
diff --git a/src/emu/cpu/tms9900/9900dasm.c b/src/emu/cpu/tms9900/9900dasm.c deleted file mode 100644 index 7b3153e6c4c..00000000000 --- a/src/emu/cpu/tms9900/9900dasm.c +++ /dev/null @@ -1,812 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Raphael Nabet -/***************************************************************************** - * - * 9900dasm.c - * TMS 9900 family disassembler - * - * Copyright Raphael Nabet - * Based on previous work Copyright John Butler. - * Based on 6502dasm.c 6502/65c02/6510 disassembler by Juergen Buchmueller - * - * - This source code is released as freeware for non-commercial purposes. - * - You are free to use and redistribute this code in modified or - * unmodified form, provided you list me in the credits. - * - If you modify this source code, you must add a notice to each modified - * source file that it has been changed. If you're a nice person, you - * will clearly mark each change too. :) - * - The author of this copywritten work reserves the right to change the - * terms of its usage and license at any time, including retroactively - * - This entire notice must remain in the source code. - * - *****************************************************************************/ - - -#include "emu.h" -#include "debugger.h" -#include "tms9900.h" - -#define MASK 0x0000ffff -#define BITS(val,n1,n2) ((val>>(15-(n2))) & (MASK>>(15-((n2)-(n1))))) - -enum format_t -{ - format_1, /* 2 address instructions */ - format_2a, /* jump instructions */ - format_2b, /* bit I/O instructions */ - format_3_9, /* logical, multiply, and divide instructions */ - format_4, /* CRU instructions */ - format_5, /* register shift instructions */ - format_6, /* single address instructions */ - format_7, /* instructions without operands */ - format_8a, /* immediate instructions (destination register) */ - format_8b, /* immediate instructions (no destination register) */ - format_9, /* extended operation instruction */ - format_10, /* memory map file instruction */ - format_11, /* multiple precision instructions */ - format_12, /* string instructions */ - format_13, /* multiple precision shift instructions */ - format_14, /* bit testing instructions */ - format_15, /* invert order of field instruction */ - format_16, /* field instructions */ - format_17, /* alter register and jump instructions */ - format_18, /* single register operand instructions */ - format_liim,/* format for liim (looks like format 18) */ - format_19, /* move address instruction */ - format_20, /* list search instructions */ - format_21, /* extend precision instruction */ - - illegal -}; - -/* definitions for flags */ -enum -{ - /* processor set on which opcodes are available */ - ps_any = 0x01, /* every processor in the tms9900/ti990 family */ - ps_mapper = 0x02, /* processors with memory mapper (ti990/10, ti990/12, - and tms99000 with mapper coprocessor) */ - ps_tms9995 = 0x04, /* ti990/12, tms9995, and later */ - ps_tms99000 = 0x08, /* ti990/12, tms99000, and later */ - ps_ti990_12 = 0x10, /* ti990/12 only */ - - /* additional flags for special decoding */ - sd_11 = 0x100, /* bit 11 should be cleared in li, ai, andi, ori, ci, stwp, stst */ - sd_11_15 = 0x200 /* bits 11-15 should be cleared in lwpi, limi, idle, rset, rtwp, ckon, ckof, lrex */ -}; - -struct description_t -{ - const char *mnemonic; - format_t format; - int flags; -}; - - -enum opcodes { - /* basic instruction set */ - _a=0, _ab, _c, _cb, _s, _sb, _soc, _socb, _szc, _szcb, - _mov, _movb, _coc, _czc, _xor, _mpy, _div, _xop, _b, _bl, - _blwp, _clr, _seto, _inv, _neg, _abs, _swpb, _inc, _inct, _dec, - _dect, _x, _ldcr, _stcr, _sbo, _sbz, _tb, _jeq, _jgt, _jh, - _jhe, _jl, _jle, _jlt, _jmp, _jnc, _jne, _jno, _joc, _jop, - _sla, _sra, _src, _srl, _ai, _andi, _ci, _li, _ori, _lwpi, - _limi, _stst, _stwp, _rtwp, _idle, _rset, _ckof, _ckon, _lrex, - - /* mapper instruction set */ - _lds, _ldd, _lmf, - - /* tms9995 instruction set */ - _divs, _mpys, _lst, _lwp, - - /* tms99000 instruction set */ - _bind, - - /* ti990/12 instruction set */ - _sram, _slam, _rto, _lto, _cnto, _slsl, _slsp, _bdc, _dbc, _swpm, - _xorm, _orm, _andm, _sm, _am, _mova, _emd, _eint, _dint, _stpc, - _cs, _seqb, _movs, _lim, _lcs, _blsk, _mvsr, _mvsk, _pops, _pshs, - - _cri, _cdi, _negr, _negd, _cre, _cde, _cer, _ced, _nrm, _tmb, - _tcmb, _tsmb, _srj, _arj, _xit, _insf, _xv, _xf, _ar, _cir, - _sr, _mr, _dr, _lr, _str, _iof, _sneb, _crc, _ts, _ad, - _cid, _sd, _md, _dd, _ld, _std, _ep, - - /* tms9940-only instruction set */ - _liim, _dca, _dcs, - - _ill -}; - - -static const description_t descriptions[144+3+1] = -{ - /* basic instruction set */ - { "a", format_1, ps_any }, { "ab", format_1, ps_any }, - { "c", format_1, ps_any }, { "cb", format_1, ps_any }, - { "s", format_1, ps_any }, { "sb", format_1, ps_any }, - { "soc", format_1, ps_any }, { "socb", format_1, ps_any }, - { "szc", format_1, ps_any }, { "szcb", format_1, ps_any }, - { "mov", format_1, ps_any }, { "movb", format_1, ps_any }, - { "coc", format_3_9, ps_any }, { "czc", format_3_9, ps_any }, - { "xor", format_3_9, ps_any }, { "mpy", format_3_9, ps_any }, - { "div", format_3_9, ps_any }, { "xop", format_9, ps_any }, - { "b", format_6, ps_any }, { "bl", format_6, ps_any }, - { "blwp", format_6, ps_any }, { "clr", format_6, ps_any }, - { "seto", format_6, ps_any }, { "inv", format_6, ps_any }, - { "neg", format_6, ps_any }, { "abs", format_6, ps_any }, - { "swpb", format_6, ps_any }, { "inc", format_6, ps_any }, - { "inct", format_6, ps_any }, { "dec", format_6, ps_any }, - { "dect", format_6, ps_any }, { "x", format_6, ps_any }, - { "ldcr", format_4, ps_any }, { "stcr", format_4, ps_any }, - { "sbo", format_2b, ps_any }, { "sbz", format_2b, ps_any }, - { "tb", format_2b, ps_any }, { "jeq", format_2a, ps_any }, - { "jgt", format_2a, ps_any }, { "jh", format_2a, ps_any }, - { "jhe", format_2a, ps_any }, { "jl", format_2a, ps_any }, - { "jle", format_2a, ps_any }, { "jlt", format_2a, ps_any }, - { "jmp", format_2a, ps_any }, { "jnc", format_2a, ps_any }, - { "jne", format_2a, ps_any }, { "jno", format_2a, ps_any }, - { "joc", format_2a, ps_any }, { "jop", format_2a, ps_any }, - { "sla", format_5, ps_any }, { "sra", format_5, ps_any }, - { "src", format_5, ps_any }, { "srl", format_5, ps_any }, - { "ai", format_8a, ps_any|sd_11 }, { "andi", format_8a, ps_any|sd_11 }, - { "ci", format_8a, ps_any|sd_11 }, { "li", format_8a, ps_any|sd_11 }, - { "ori", format_8a, ps_any|sd_11 }, { "lwpi", format_8b, ps_any|sd_11|sd_11_15 }, - { "limi", format_8b, ps_any|sd_11|sd_11_15 }, { "stst", format_18, ps_any|sd_11 }, - { "stwp", format_18, ps_any|sd_11 }, { "rtwp", format_7, ps_any|sd_11|sd_11_15 }, - { "idle", format_7, ps_any|sd_11|sd_11_15 }, { "rset", format_7, ps_any|sd_11|sd_11_15 }, - { "ckof", format_7, ps_any|sd_11|sd_11_15 }, { "ckon", format_7, ps_any|sd_11|sd_11_15 }, - { "lrex", format_7, ps_any|sd_11|sd_11_15 }, - - /* mapper instruction set */ - { "lds", format_6, ps_mapper }, { "ldd", format_6, ps_mapper }, - { "lmf", format_10, ps_mapper }, - - /* tms9995 instruction set */ - { "divs", format_6, ps_tms9995 }, { "mpys", format_6, ps_tms9995 }, - { "lst", format_18, ps_tms9995 }, { "lwp", format_18, ps_tms9995 }, - - /* tms99000 instruction set */ - { "bind", format_6, ps_tms99000 }, - - /* ti990/12 instruction set */ - { "sram", format_13, ps_ti990_12 }, { "slam", format_13, ps_ti990_12 }, - { "rto", format_11, ps_ti990_12 }, { "lto", format_11, ps_ti990_12 }, - { "cnto", format_11, ps_ti990_12 }, { "slsl", format_20, ps_ti990_12 }, - { "slsp", format_20, ps_ti990_12 }, { "bdc", format_11, ps_ti990_12 }, - { "dbc", format_11, ps_ti990_12 }, { "swpm", format_11, ps_ti990_12 }, - { "xorm", format_11, ps_ti990_12 }, { "orm", format_11, ps_ti990_12 }, - { "andm", format_11, ps_ti990_12 }, { "sm", format_11, ps_ti990_12 }, - { "am", format_11, ps_ti990_12 }, { "mova", format_19, ps_ti990_12 }, - { "emd", format_7, ps_ti990_12 }, { "eint", format_7, ps_ti990_12 }, - { "dint", format_7, ps_ti990_12 }, { "stpc", format_18, ps_ti990_12 }, - { "cs", format_12, ps_ti990_12 }, { "seqb", format_12, ps_ti990_12 }, - { "movs", format_12, ps_ti990_12 }, { "lim", format_18, ps_ti990_12 }, - { "lcs", format_18, ps_ti990_12 }, { "blsk", format_8a, ps_ti990_12 }, - { "mvsr", format_12, ps_ti990_12 }, { "mvsk", format_12, ps_ti990_12 }, - { "pops", format_12, ps_ti990_12 }, { "pshs", format_12, ps_ti990_12 }, - { "cri", format_7, ps_ti990_12 }, { "cdi", format_7, ps_ti990_12 }, - { "negr", format_7, ps_ti990_12 }, { "negd", format_7, ps_ti990_12 }, - { "cre", format_7, ps_ti990_12 }, { "cde", format_7, ps_ti990_12 }, - { "cer", format_7, ps_ti990_12 }, { "ced", format_7, ps_ti990_12 }, - { "nrm", format_11, ps_ti990_12 }, { "tmb", format_14, ps_ti990_12 }, - { "tcmb", format_14, ps_ti990_12 }, { "tsmb", format_14, ps_ti990_12 }, - { "srj", format_17, ps_ti990_12 }, { "arj", format_17, ps_ti990_12 }, - { "xit", format_7, ps_ti990_12 }, { "insf", format_16, ps_ti990_12 }, - { "xv", format_16, ps_ti990_12 }, { "xf", format_16, ps_ti990_12 }, - { "ar", format_6, ps_ti990_12 }, { "cir", format_6, ps_ti990_12 }, - { "sr", format_6, ps_ti990_12 }, { "mr", format_6, ps_ti990_12 }, - { "dr", format_6, ps_ti990_12 }, { "lr", format_6, ps_ti990_12 }, - { "str", format_6, ps_ti990_12 }, { "iof", format_15, ps_ti990_12 }, - { "sneb", format_12, ps_ti990_12 }, { "crc", format_12, ps_ti990_12 }, - { "ts", format_12, ps_ti990_12 }, { "ad", format_6, ps_ti990_12 }, - { "cid", format_6, ps_ti990_12 }, { "sd", format_6, ps_ti990_12 }, - { "md", format_6, ps_ti990_12 }, { "dd", format_6, ps_ti990_12 }, - { "ld", format_6, ps_ti990_12 }, { "std", format_6, ps_ti990_12 }, - { "ep", format_21, ps_ti990_12 }, - - /* tms9940-only instruction set */ - /* these instructions are said to be format 9 (xop), but since the xop - level is interpreted as part of the opcode, dca and dcs should be handled - like format 6. liim looks like format 18, but slightly different, - therefore it is handled like a special format. */ - { "liim", format_liim,/*ps_tms9940*/0 }, { "dca", format_6, /*ps_tms9940*/0 }, - { "dcs", format_6, /*ps_tms9940*/0 }, - - { NULL, illegal, ps_any } -}; - - -static const enum opcodes ops_4000_ffff_s12[12]= -{ - _szc, _szcb, _s, _sb, /*4000-7000*/ - _c, _cb, _a, _ab, _mov, _movb, _soc, _socb /*8000-f000*/ -}; - - -static const enum opcodes ops_2000_3fff_s10[8]= -{ - _coc, _czc, _xor, _xop, _ldcr, _stcr, _mpy, _div /*2000-3800*/ -}; - - -static const enum opcodes ops_1000_1fff_s8[16]= -{ - _jmp, _jlt, _jle, _jeq, _jhe, _jgt, _jne, _jnc, /*1000-1700*/ - _joc, _jno, _jl, _jh, _jop, _sbo, _sbz, _tb /*1800-1f00*/ -}; - - -static const enum opcodes ops_0e40_0fff_s6[7]= -{ - _ad, _cid, _sd, _md, _dd, _ld, _std /*0e40-0fc0*/ -}; - - -static const enum opcodes ops_0e00_0e3f_s4[4]= -{ - _iof, _sneb, _crc, _ts /*0e00-0e30*/ -}; - - -static const enum opcodes ops_0c40_0dff_s6[7]= -{ - _ar, _cir, _sr, _mr, _dr, _lr, _str /*0c40-0dc0*/ -}; - - -static const enum opcodes ops_0c10_0c3f_s4[3]= -{ - _insf, _xv, _xf /*0c10-0c30*/ -}; - - -static const enum opcodes ops_0c00_0c0f_s0[16]= -{ - _cri, _cdi, _negr, _negd, _cre, _cde, _cer, _ced, /*0c00-0c07*/ - _nrm, _tmb, _tcmb, _tsmb, _srj, _arj, _xit, _xit /*0c08-0c0f*/ -}; - - - -static const enum opcodes ops_0800_0bff_s8[4]= -{ - _sra, _srl, _sla, _src /*0800-0b00*/ -}; - - -static const enum opcodes ops_0400_07ff_s6[16]= -{ - _blwp, _b, _x, _clr, _neg, _inv, _inc, _inct, /*0400-05c0*/ - _dec, _dect, _bl, _swpb, _seto, _abs, _lds, _ldd /*0600-07c0*/ -}; - - -static const enum opcodes ops_0200_03ff_s5[16]= -{ - _li, _ai, _andi, _ori, _ci, _stwp, _stst, _lwpi, /*0200-02e0*/ - _limi, _lmf, _idle, _rset, _rtwp, _ckon, _ckof, _lrex /*0300-03e0*/ -}; - - -static const enum opcodes ops_0100_01ff_s6[4]= -{ - _ill, _bind, _divs, _mpys /*0100-01c0*/ -}; - - -static const enum opcodes ops_0030_00ff_s4[13]= -{ - _stpc, _cs, _seqb, _movs, _lim, /*0030-0070*/ - _lst, _lwp, _lcs, _blsk, _mvsr, _mvsk, _pops, _pshs /*0080-00f0*/ -}; - - -static const enum opcodes ops_001c_002f_s0[20]= -{ - _sram, _slam, _rto, _lto, /*001c-001f*/ - _cnto, _slsl, _slsp, _bdc, _dbc, _swpm, _xorm, _orm, /*0020-0027*/ - _andm, _sm, _am, _mova, _ill, _emd, _eint, _dint /*0028-002f*/ -}; - - - -static int PC; - - -INLINE UINT16 readop_arg(const UINT8 *opram, unsigned pc) -{ - UINT16 result = opram[PC++ - pc] << 8; - return result | opram[PC++ - pc]; -} - -static int print_arg (char *dest, int mode, int arg, const UINT8 *opram, unsigned pc) -{ - int base; - - switch (mode) - { - case 0x0: /* workspace register */ - return sprintf (dest, "R%d", arg); - case 0x1: /* workspace register indirect */ - return sprintf (dest, "*R%d", arg); - case 0x2: /* symbolic|indexed */ - base = readop_arg(opram, pc); - if (arg) /* indexed */ - return sprintf (dest, "@>%04x(R%d)", base, arg); - else /* symbolic (direct) */ - return sprintf (dest, "@>%04x", base); - case 0x3: /* workspace register indirect auto increment */ - return sprintf (dest, "*R%d+", arg); - } - - return 0; -} - - -/***************************************************************************** - * Disassemble a single command and return the number of bytes it uses. - *****************************************************************************/ -unsigned Dasm9900 (char *buffer, unsigned pc, int model_id, const UINT8 *oprom, const UINT8 *opram) -{ - int OP, OP2, opc; - int sarg, darg, smode, dmode; - signed char displacement; - int byte_count, checkpoint; - int bit_position, bit_width; - unsigned dasmflags = 0; - - const char *mnemonic; - format_t format; - int flags; - - /* - Under tms9900, opcodes >0400->07FF are incompletely decoded: bits 11 is ignored, and so are - bits 12-15 for instructions which do not require a register. On the other hand, ti990/10 - generates an illegal instruction error when bit 11 is set, but still ignores bits 12-15. - Additionally, ti990/12 and tms9995 will generate an illegal error when bits 12-15 are - non-zero. - */ - #define BETTER_0200_DECODING (model_id == TI990_10_ID) - #define COMPLETE_0200_DECODING (/*(model_id == TI990_12_ID) ||*/ (model_id >= TMS9995_ID)) - - int processor_mask = ps_any; - - if ((model_id == TI990_10_ID) /*|| (model_id == TI990_12_ID)*/ || (model_id >= TMS99000_ID)) - processor_mask |= ps_mapper; /* processors with memory mapper (ti990/10, ti990/12, - and tms99000 with mapper coprocessor) */ - if (/*(model_id == TI990_12_ID) ||*/ (model_id >= TMS9995_ID)) - processor_mask |= ps_tms9995; /* ti990/12, tms9995, and later */ - - if (/*(model_id == TI990_12_ID) ||*/ (model_id >= TMS99000_ID)) - processor_mask |= ps_tms99000; /* ti990/12, tms99000, and later */ - - /*if ((model_id == TI990_12_ID)) - processor_mask |= ps_ti990_12;*/ /* ti990/12, tms99000, and later */ - - PC = pc; - OP = oprom[PC++ - pc] << 8; - OP |= oprom[PC++ - pc]; - - /* let's identify the opcode */ - if (OP >= 0x4000) - opc = ops_4000_ffff_s12[(OP - 0x4000) >> 12]; - else if (OP >= 0x2000) - opc = ops_2000_3fff_s10[(OP - 0x2000) >> 10]; - else if (OP >= 0x1000) - opc = ops_1000_1fff_s8[(OP - 0x1000) >> 8]; - else if (OP >= 0x0C00) - { - if (OP >= 0x0E40) - opc = ops_0e40_0fff_s6[(OP - 0x0E40) >> 6]; - else if (OP >= 0x0E00) - opc = ops_0e00_0e3f_s4[(OP - 0x0E00) >> 4]; - else if (OP >= 0x0C40) - opc = ops_0c40_0dff_s6[(OP - 0x0C40) >> 6]; - else if (OP >= 0x0C10) - opc = ops_0c10_0c3f_s4[(OP - 0x0C10) >> 4]; - else - opc = ops_0c00_0c0f_s0[OP - 0x0C00]; - } - else if (OP >= 0x0800) - opc = ops_0800_0bff_s8[(OP - 0x0800) >> 8]; - else if (OP >= 0x0400) - opc = ops_0400_07ff_s6[(OP - 0x0400) >> 6]; - else if (OP >= 0x0200) - { - opc = ops_0200_03ff_s5[(OP - 0x0200) >> 5]; - if (BETTER_0200_DECODING || COMPLETE_0200_DECODING) - { - flags = descriptions[opc].flags; - if ( (COMPLETE_0200_DECODING && (flags & sd_11_15) && (OP & 0x001f)) - || ((flags & sd_11) && (OP & 0x0010)) ) - { - opc = _ill; - if (OP >= 0x03f0) - opc = _ep; /* the ep opcode is located in a "hole" */ - } - } - } - else if (OP >= 0x0100) - opc = ops_0100_01ff_s6[(OP - 0x0100) >> 6]; - else if (OP >= 0x0030) - opc = ops_0030_00ff_s4[(OP - 0x0030) >> 4]; - else if (OP >= 0x001C) - opc = ops_001c_002f_s0[OP - 0x001C]; - else - opc = _ill; - - /* read flags */ - flags = descriptions[opc].flags; - /* set as illegal if the processor does not implement this instruction */ - if (! (flags & processor_mask)) - { - opc = _ill; - flags = descriptions[opc].flags; /* read new flags */ - } - - /* tms9940 replace a few xops with custom instructions */ - if ((opc == _xop) && ((model_id == TMS9940_ID) || (model_id == TMS9985_ID))) - { - switch (BITS(OP,6,9)) - { - case 0: - /* opcode is dca */ - opc = _dca; - break; - - case 1: - /* opcode is dcs */ - opc = _dcs; - break; - - case 2: - case 3: /* should be 2, but instruction decoding is incomplete */ - /* opcode is liim */ - if (BITS(OP,12,15) == 0) - /* ts must be == 0 */ - opc = _liim; - else - /* I don't know what happens when ts != 0. Maybe the CPU does - the complete address decoding, and liim gets a bogus value - instead of the immediate. Since I do not know, I handle this - as an illegal instruction. */ - opc = _ill; - break; - - default: - /* this is still a software xop */ - break; - } - } - - mnemonic = descriptions[opc].mnemonic; - format = descriptions[opc].format; - - /* bl and blwp instructions are subroutines */ - if (mnemonic != NULL && mnemonic[0] == 'b' && mnemonic[1] == 'l') - dasmflags = DASMFLAG_STEP_OVER; - - /* b *r11 and rtwp are returns */ - else if (opc == 0x045b || (mnemonic != NULL && strcmp(mnemonic, "rtwp") == 0)) - dasmflags = DASMFLAG_STEP_OUT; - - switch (format) - { - case format_1: /* 2 address instructions */ - smode = BITS(OP,10,11); - sarg = BITS(OP,12,15); - dmode = BITS(OP,4,5); - darg = BITS(OP,6,9); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - break; - - case format_2a: /* jump instructions */ - displacement = (signed char)BITS(OP,8,15); - sprintf (buffer, "%-4s >%04x", mnemonic, 0xffff & (PC + displacement * 2)); - break; - - case format_2b: /* bit I/O instructions */ - displacement = (signed char)BITS(OP,8,15); - sprintf (buffer, "%-4s >%04x", mnemonic, 0xffff & displacement); - break; - - case format_3_9: /* logical, multiply, and divide instructions */ - case format_4: /* CRU instructions */ - case format_9: /* extended operation instruction */ - smode = BITS(OP,10,11); - sarg = BITS(OP,12,15); - darg = BITS(OP,6,9); - - if ((darg == 0) && (format == format_4)) - darg = 16; - - if (format == format_3_9) - { - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ",R%d", darg); - } - else - { - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ",%d", darg); - } - break; - - case format_5: /* register shift instructions */ - sarg = BITS(OP,12,15); - darg = BITS(OP,8,11); - - sprintf (buffer, darg ? "%-4s R%d,%d" : "%-4s R%d,R%d", mnemonic, sarg, darg); - break; - - case format_6: /* single address instructions */ - smode = BITS(OP,10,11); - sarg = BITS(OP,12,15); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - break; - - case format_7: /* instructions without operands */ - sprintf (buffer, "%s", mnemonic); - break; - - case format_8a: /* immediate instructions (destination register) */ - darg = BITS(OP,12,15); - sarg = readop_arg(opram, pc); - - sprintf (buffer, "%-4s R%d,>%04x", mnemonic, darg, sarg); - break; - - case format_8b: /* immediate instructions (no destination register) */ - sarg = readop_arg(opram, pc); - - sprintf (buffer, "%-4s >%04x", mnemonic, sarg); - break; - - case format_10: /* memory map file instruction */ - sarg = BITS(OP,12,15); - darg = BITS(OP,11,11); - - sprintf (buffer, "%-4s R%d,%d", mnemonic, sarg, darg); - break; - - case format_11: /* multiple precision instructions */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - dmode = BITS(OP2,4,5); - darg = BITS(OP2,6,9); - byte_count = BITS(OP2,0,3); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - buffer += sprintf (buffer, byte_count ? ",%d" : ",R%d", byte_count); - break; - - case format_12: /* string instructions */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - dmode = BITS(OP2,4,5); - darg = BITS(OP2,6,9); - byte_count = BITS(OP2,0,3); - checkpoint = BITS(OP,12,15); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - buffer += sprintf (buffer, byte_count ? ",%d,R%d" : ",R%d,R%d", byte_count, checkpoint); - break; - - case format_13: /* multiple precision shift instructions */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - darg = BITS(OP2,6,9); - byte_count = BITS(OP2,0,3); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, byte_count ? ",%d" : ",R%d", byte_count); - buffer += sprintf (buffer, darg ? ",%d" : ",R%d", darg); - break; - - case format_14: /* bit testing instructions */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - darg = BITS(OP2,0,9); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - if (darg == 0x3ff) - buffer += sprintf (buffer, ",R0"); - else - buffer += sprintf (buffer, ",%d", darg); - break; - - case format_15: /* invert order of field instruction */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - bit_position = BITS(OP2,0,3); - bit_width = BITS(OP,12,15); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, bit_position ? ",(%d," : ",(R%d,", bit_position); - buffer += sprintf (buffer, bit_width ? "%d)" : "R%d)", bit_width); - break; - - case format_16: /* field instructions */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - dmode = BITS(OP2,4,5); - darg = BITS(OP2,6,9); - bit_position = BITS(OP2,0,3); - bit_width = BITS(OP,12,15); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - buffer += sprintf (buffer, bit_position ? ",(%d," : ",(%d,", bit_position); - buffer += sprintf (buffer, bit_width ? "%d)" : "R%d)", bit_width); - break; - - case format_17: /* alter register and jump instructions */ - OP2 = readop_arg(opram, pc); - - displacement = (signed char)BITS(OP2,8,15); - sarg = BITS(OP2,4,7); - darg = BITS(OP2,0,3); - if (darg) - sprintf (buffer, darg ? "%-4s >%04x,%d,R%d" : "%-4s >%04x,R%d,R%d", - mnemonic, 0xffff & (PC + displacement * 2), sarg, darg); - break; - - case format_18: /* single register operand instructions */ - sarg = BITS(OP,12,15); - - sprintf (buffer, "%-4s R%d", mnemonic, sarg); - break; - - case format_liim: /* liim instruction */ - sarg = BITS(OP,14,15); - - sprintf (buffer, "%-4s %d", mnemonic, sarg); - break; - - case format_19: /* move address instruction */ - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - dmode = BITS(OP2,4,5); - darg = BITS(OP2,6,9); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - break; - - case format_20: /* list search instructions */ - { - const char *condition_code; - - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - dmode = BITS(OP2,4,5); - darg = BITS(OP2,6,9); - - switch (BITS(OP2,0,3)) - { - case 0: - condition_code = "eq"; - break; - case 1: - condition_code = "ne"; - break; - case 2: - condition_code = "he"; - break; - case 3: - condition_code = "l"; - break; - case 4: - condition_code = "ge"; - break; - case 5: - condition_code = "lt"; - break; - case 6: - condition_code = "le"; - break; - case 7: - condition_code = "h"; - break; - case 8: - condition_code = "lte"; - break; - case 9: - condition_code = "gt"; - break; - default: - condition_code = "??"; - break; - } - - buffer += sprintf (buffer, "%-4s %s,", mnemonic, condition_code); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - break; - } - - case format_21: /* extend precision instruction */ - { - int dest_byte_count; - - OP2 = readop_arg(opram, pc); - - smode = BITS(OP2,10,11); - sarg = BITS(OP2,12,15); - dmode = BITS(OP2,4,5); - darg = BITS(OP2,6,9); - byte_count = BITS(OP2,0,3); - dest_byte_count = BITS(OP,12,15); - - buffer += sprintf (buffer, "%-4s ", mnemonic); - buffer += print_arg (buffer, smode, sarg, opram, pc); - buffer += sprintf (buffer, ","); - buffer += print_arg (buffer, dmode, darg, opram, pc); - buffer += sprintf (buffer, byte_count ? ",%d" : ",R%d", byte_count); - buffer += sprintf (buffer, dest_byte_count ? ",%d" : ",R%d", dest_byte_count); - break; - } - - default: - logerror("debbugger internal error, file %s, line %d\n", __FILE__, __LINE__); - case illegal: - sprintf (buffer, "data >%04x", OP); - break; - } - - return (PC - pc) | DASMFLAG_SUPPORTED | dasmflags; -} - -CPU_DISASSEMBLE( tms9900 ) -{ - return Dasm9900(buffer, pc, TMS9900_ID, oprom, opram); -} - -CPU_DISASSEMBLE( tms9980 ) -{ - return Dasm9900(buffer, pc, TMS9980_ID, oprom, opram); -} - -CPU_DISASSEMBLE( tms9995 ) -{ - return Dasm9900(buffer, pc, TMS9995_ID, oprom, opram); -} diff --git a/src/emu/cpu/tms9900/99xxcore.h b/src/emu/cpu/tms9900/99xxcore.h deleted file mode 100644 index cb94fac286f..00000000000 --- a/src/emu/cpu/tms9900/99xxcore.h +++ /dev/null @@ -1,5319 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Raphael Nabet - -/*************************************************************************** - Legacy TMS99xx core implementation - - PLEASE DO NOT REMOVE THIS FILE from the source tree, even if none of the - drivers make use of it anymore. It still contains documentation and - incomplete implementations of cores that are not (yet) used in drivers. - - It need not be included in the build process, however. - - Michael Zapf, February 2014 - -***************************************************************************/ - -/* - 99xxcore.h : generic tms99xx emulation - - The TMS99XX_MODEL switch tells which emulator we want to build. Set the - switch, then include 99xxcore.h, and you will have an emulator for this - processor. - - Only ti990/10, tms9900, tms9980a/9981, and tms9995 work OK for now. Note - that tms9995 has not been tested extensively. - - tms9940 is WIP: it is probably still buggy (particularily the BCD support), - as it has not been tested. tms9985 has been implemented as a 9940 with - a data bus, which should be mostly correct. - - I think all software aspects of tms9985 and tms9989 are implemented. - You just need to implement bus interfaces, provided you know them. - (I don't...) - - tms99000 cannot be implemented fully yet, due to lack of documentation. - - ti990/12 is not supported at all, and it should probably be implemented as - a separate processor core, anyway. - - Original tms9900 emulator by Edward Swartz - Smoothed out by Raphael Nabet - Originally converted for Mame by M.Coates - Processor timing, support for tms9980 and tms9995, and many bug fixes by R Nabet -*/ - -/* - The first member of the family was actually the ti990/10 minicomputer, - released in 1975. tms9900 was released in 1976, and has the same - instruction set as ti990/10: however, tms9900 is slower, it does not - support privileges and memory mapping, and illegal instructions do not - cause an error interrupt. - - The ti990 family later evoluted into the huge ti990/12 system, with support - for 144 different instructions, and microcode programming in case some user - found it was not enough. ti990/10 was eventually replaced by a cheaper - ti990/10a board, built around a tms99000 microprocessor. - - The tms9980 processor is merely a tms9900 with a 8-bit data bus (instead of - 16-bit on tms9900). - - tms9940 is a microcontroller, and is mostly similar to 9900/9980. The - variant I know has 2kb of ROM, 128 bytes of RAM, a timer, 32 I/O line, some - of which can be reconfigured as a CRU bus, but no external memory bus. It - includes three additional opcodes, which are not supported by any other - member of the family (with the probable exception of TMS9985). - - tms9985 is similar to tms9940, but it supports an external 8-bit-wide - memory bus. At least one variant included 8kb of ROM, 256 bytes of RAM. - It was ill-fated, as it was never released due to technical problems. - - tms9989 is mostly alien to me. I guess it is a close relative of tms9995, - although I am not sure. I have read that the SBP68689 supports tms9995 - opcodes, but that tms9989 does not. - - tms9995 belongs to another generation. It is quite faster than tms9900, - and supports 4 extra opcodes. Its external bus is 8-bit-wide, and it has - 256 bytes of internal 16-bit RAM. - - tms99000 is the successor to both ti9900 and ti990/10. It supports - privileges, and has a coprocessor interface which enables the use of an - external memory mapper. Additionnally, it can use a Macrostore ROM to - emulate additional instructions. - - This feature allowed TI to expand the 99000 family with the tms99105 (which - was said to support 84 instructions types), the tms99110 (which supported - floating point instructions), and possibly another chip (tms99220???) which - included parts of the UCSD P-system in Macrostore. - -References : -* 9900 family systems design, chapter 6, 7, 8 -* TMS 9980A/ TMS 9981 Product Data Book -* TMS 9995 16-Bit Microcomputer Data Manual -* Model 990/10A computer general description, section 4 (p/n 2302633-9701 A) -* 990/99000 assembly language reference manual (p/n 2270509-9701 A) -* Chapter 18 of unidentified book is the only reference on tms9940 I have found so far (Paperport format) - <ftp://ftp.whtech.com//datasheets/Hardware manuals/tms9900 9901 9904 9940 9980 (by a osborne).max> - -Other references can be found on spies.com: -<http://www.spies.com/arcade/simulation/processors/index.html> -<http://www.spies.com/~aek/pdf/ti/> - -*/ - - -#if (TMS99XX_MODEL == TI990_10_ID) - - #define TMS99XX_PREFIX ti990_10 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( ti990_10l ) - #define TMS99XX_device_get_name "TI990/10L" - #define TMS99XX_device_get_shortname "ti990_10l" - -#elif (TMS99XX_MODEL == TMS9900_ID) - - #define TMS99XX_PREFIX tms9900 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms9900l ) - #define TMS99XX_device_get_name "TMS9900L" - #define TMS99XX_device_get_shortname "tms9900l" - -#elif (TMS99XX_MODEL == TMS9940_ID) - - #define TMS99XX_PREFIX tms9940 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms9940l ) - #define TMS99XX_device_get_name "TMS9940L" - #define TMS99XX_device_get_shortname "tms9940l" - - #error "tms9940 is not yet supported" - -#elif (TMS99XX_MODEL == TMS9980_ID) - - #define TMS99XX_PREFIX tms9980a - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms9980al ) - #define TMS99XX_device_get_name "TMS9980AL/TMS9981L" - #define TMS99XX_device_get_shortname "tms9980al" - -#elif (TMS99XX_MODEL == TMS9985_ID) - - #define TMS99XX_PREFIX tms9985 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms9985l ) - #define TMS99XX_device_get_name "TMS9985L" - #define TMS99XX_device_get_shortname "tms9985l" - - #error "tms9985 is not yet supported" - -#elif (TMS99XX_MODEL == TMS9989_ID) - - #define TMS99XX_PREFIX tms9989 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms9989l ) - #define TMS99XX_device_get_name "TMS9989L" - #define TMS99XX_device_get_shortname "tms9989l" - - #error "tms9989 is not yet supported" - -#elif (TMS99XX_MODEL == TMS9995_ID) - - #define TMS99XX_PREFIX tms9995 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms9995l ) - #define TMS99XX_device_get_name "TMS9995L" - #define TMS99XX_device_get_shortname "tms9995l" - -#elif (TMS99XX_MODEL == TMS99000_ID) - - #define TMS99XX_PREFIX tms99000 - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms99000l ) - #define TMS99XX_device_get_name "TMS99000L" - #define TMS99XX_device_get_shortname "tms99000l" - - #error "tms99000 is not yet supported" - -#elif (TMS99XX_MODEL == TMS99105A_ID) - - #define TMS99XX_PREFIX tms99105a - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms99105al ) - #define TMS99XX_device_get_name "TMS99105AL" - #define TMS99XX_device_get_shortname "tms99105al" - - #error "tms99105a is not yet supported" - -#elif (TMS99XX_MODEL == TMS99110A_ID) - - #define TMS99XX_PREFIX tms99110a - #define TMS99XX_GET_INFO CPU_GET_INFO_NAME( tms99110al ) - #define TMS99XX_device_get_name "TMS99110AL" - #define TMS99XX_device_get_shortname "tms99110al" - - #error "tms99110a is not yet supported" - -#endif - -/* - Now for some preprocessor wizardry. -*/ -#define concat2(a,b) a##b - -#define GET_INFO(prefix) concat2(prefix,_get_info) -#define RESET_PARAM(prefix) concat2(prefix,reset_param) - -#define TMS99XX_RESET_PARAM RESET_PARAM(TMS99XX_PREFIX) - - - -/* - I include this macro because we may eventually support other 99000 variants such as tms99110, - and this macro will remain true for every 99000 family member, even when we have - (TMS99XX_MODEL != TMS99000_ID). -*/ -#define IS_99000 (TMS99XX_MODEL == TMS99000_ID) - -/* - On microprocessor implementations (other than TMS9940 and, probably, TMS9985), the CKOF, CKON, - IDLE, LREX and RSET cause an external CRU write. CKOF, CKON and LREX do nothing apart of this, - therefore they must be implemented with external hardware (CKON and CKOF are supposed to - enable/disable a line clock interrupt, and LREX to trigger a LOAD interrupt). IDLE and RSET - are functional, but, on the one hand, the design allowed to light a diagnostic LED when - the processor is in IDLE state, and, on the other hand, the RSET instruction is supposed - to reset external devices as well. - - On the TI990/10 and TI990/12 minicomputers, there is no such CRU write. The line clock - interrupt latch is part of the CPU board, LREX is fully functional, the IDLE led is connected - to the board, and the RSET line is part of the TILINE bus connector. - - On the TMS9940, CKOF, CKON, LREX and RSET are not supported. IDLE, on the other hand, is - supported, and the CPU can be configured to output its IDLE state on the P16 I/O pin. -*/ -#define EXTERNAL_INSTRUCTION_DECODING (TMS99XX_MODEL != TI990_10_ID) && (TMS99XX_MODEL != TI9940_ID) && (TMS99XX_MODEL != TI9985_ID) -#define EXTERNAL_INSTRUCTION_CALLBACK (TMS99XX_MODEL == TI990_10_ID) - -/* - ti990/10, ti990/12 and tms99000 support privileges - - privileged instructions: - CKOF, CKON, IDLE, LIMI, LREX, RSET, - LDD, LDS, LMF, (memory mapping instructions) - DINT, EINT, EMD , LCS, LIM, SLSP. (990/12 instructions) - - instructions which are privileged when the effective CRU address is higher than 0xE00: - LDCR, SBO, SBZ, STCR, TB. - - instructions whose behaviour is modified in user mode (only user flags in ST are affected): - RTWP, - LST. (ti990/12 and tms99000 instruction) -*/ -#define HAS_PRIVILEGE ((TMS99XX_MODEL == TI990_10_ID) || IS_99000) - -/* - opcode groups - - * 69 basic opcodes implemented on all family members - * 3 memory mapping opcodes implemented on ti990/10 with mapping option, ti990/12, and - the tim99610 mapper in conjunction with any tms99000 family member: LMF, LDS, LDD - * 3 opcodes implemented on tms9940 (and probably tms9985) only: DCA, DCS, LIIM - * 4 opcodes implemented on ti990/12, tms9989 and above: MPYS, DIVS, LST, LWP - * 1 opcode implemented on ti990/12, and tms99000: BIND - * 72 opcodes implemented on ti990/12 only (some of which are emulated by tms99105 & tms99110) -*/ - -#define HAS_MAPPING ((TMS99XX_MODEL == TI990_10_ID) /*|| IS_99000*/) -#define HAS_9995_OPCODES ((TMS99XX_MODEL == TMS9989_ID) || (TMS99XX_MODEL == TMS9995_ID) || IS_99000) -#define HAS_BIND_OPCODE IS_99000 - -#define HAS_OVERFLOW_INTERRUPT ((TMS99XX_MODEL == TMS9995_ID) || IS_99000) - -/* - Under tms9900, opcodes >0200->03FF are incompletely decoded: bits 11 is ignored, and so are - bits 12-15 for instructions which do not require a register. On the other hand, ti990/10 - generates an illegal instruction error when bit 11 is set, but still ignores bits 12-15. - Additionally, ti990/12 and tms9995 will generate an illegal error when bits 12-15 are - non-zero. -*/ -#define BETTER_0200_DECODING (TMS99XX_MODEL == TI990_10_ID) -#define COMPLETE_0200_DECODING (TMS99XX_MODEL >= TMS9995_ID) - -/* - TI990/10 and tms9900 force unused bits in the ST register to 0. - TMS9995 does not. -*/ -#define USE_ST_MASK (TMS99XX_MODEL <= TMS9985_ID) - -/* - TI990/10, TMS9900 and TMS99000 have a 16-bit-wide memory bus, and use 16-bus accessors. - TMS9940 use 16-bit accessors, too, as it has an internal 16-bit bus, and no external bus. -*/ -#define USE_16_BIT_ACCESSORS ((TMS99XX_MODEL == TI990_10_ID) || (TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9940_ID) || (IS_99000)) - -struct tms99xx_state; - -INLINE void execute(tms99xx_state *cpustate, UINT16 opcode); - -#if EXTERNAL_INSTRUCTION_DECODING -static void external_instruction_notify(tms99xx_state *cpustate, int ext_op_ID); -#endif -static UINT16 decipheraddr(tms99xx_state *cpustate, UINT16 opcode); -static UINT16 decipheraddrbyte(tms99xx_state *cpustate, UINT16 opcode); -static void contextswitch(tms99xx_state *cpustate, UINT16 addr); -#if HAS_MAPPING || HAS_PRIVILEGE -static void contextswitchX(tms99xx_state *cpustate, UINT16 addr); -#else -#define contextswitchX(cs, addr) contextswitch(cs, addr) -#endif -static void field_interrupt(tms99xx_state *cpustate); - -/***************************/ -/* Mame Interface Routines */ -/***************************/ - - -/* tms9900 ST register bits. */ - -/* These bits are set by every compare, move and arithmetic or logical operation : */ -/* (Well, COC, CZC and TB only set the E bit, but these are kind of exceptions.) */ -#define ST_LGT 0x8000 /* Logical Greater Than (strictly) */ -#define ST_AGT 0x4000 /* Arithmetical Greater Than (strictly) */ -#define ST_EQ 0x2000 /* Equal */ - -/* These bits are set by arithmetic operations, when it makes sense to update them. */ -#define ST_C 0x1000 /* Carry */ -#define ST_OV 0x0800 /* OVerflow (overflow with operations on signed integers, */ - /* and when the result of a 32bits:16bits division cannot fit in a 16-bit word.) */ - -/* This bit is set by move and arithmetic operations WHEN THEY USE BYTE OPERANDS. */ -#define ST_OP 0x0400 /* Odd Parity */ - -#if (TMS99XX_MODEL != TMS9940_ID) - -/* This bit is set by the XOP instruction. */ -#define ST_X 0x0200 /* Xop */ - -#endif - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - -/* This bit is set by arithmetic operations to support BCD */ -#define ST_DC 0x0100 /* Digit Carry */ - -#endif - -#if HAS_PRIVILEGE - -/* This bit is set in user (i.e. non-supervisor) mode */ -#define ST_PR 0x0100 /* PRivilege */ - -#endif - -#if HAS_MAPPING - -/* This tells which map is currently in use */ -#define ST_MF 0x0080 /* Map File */ - -#endif - -#if (HAS_OVERFLOW_INTERRUPT) - -/* This bit is set in ti990/12, TMS9995 and later chips to generate a level-2 interrupt when -the Overflow status bit is set */ -#define ST_OVIE 0x0020 /* OVerflow Interrupt Enable */ - -#endif - -#if (IS_99000) - -/* This bit enables the macrostore feature on tms99000 */ -/* (It is used on ti990/12 with a different meaning.) */ -#define ST_EMR 0x0010 /* Enable Macrostore Rom */ - -#endif - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - -#define ST_IM 0x0003 /* Interrupt Mask */ - -#else - -#define ST_IM 0x000F /* Interrupt Mask */ - -#endif - -/* On models before TMS9995 (TMS9989 ?), unused ST bits are always forced to 0, so we define -a ST_MASK */ -#if (USE_ST_MASK) - #if (TMS99XX_MODEL == TI990_10_ID) - #define ST_MASK 0xFF8F - #elif (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) -#define ST_MASK 0xFD03 - #else - #define ST_MASK 0xFE0F - #endif -#endif - - -/* error interrupt register bits */ - -#if (TMS99XX_MODEL == TI990_10_ID) - -/* This bit is set in user (i.e. non-supervisor) mode */ -#define EIR_MAPERR 0x0800 /* memory MAPping ERRor */ -#define EIR_MER 0x1000 /* Memory parity ERRor */ -#define EIR_ILLOP 0x2000 /* ILLegal OPcode */ -#define EIR_PRIVOP 0x4000 /* PRiviledged OPeration */ -#define EIR_TIMEOUT 0x8000 /* TILINE TIMEOUT */ - -#endif - - -/* Offsets for registers. */ -#define R0 0 -#define R1 2 -#define R2 4 -#define R3 6 -#define R4 8 -#define R5 10 -#define R6 12 -#define R7 14 -#define R8 16 -#define R9 18 -#define R10 20 -#define R11 22 -#define R12 24 -#define R13 26 -#define R14 28 -#define R15 30 - -struct map_file_t -{ - UINT16 L[3], B[3]; /* actual registers */ - UINT32 limit[3], bias[3]; /* equivalent in a more convenient form */ -}; - -struct tms99xx_state -{ -/* "actual" tms9900 registers : */ - UINT16 WP; /* Workspace pointer */ - UINT16 PC; /* Program counter */ - UINT16 STATUS; /* STatus register */ - -/* Now, data used for emulation */ - UINT8 lastparity; - char lds_flag, ldd_flag; - UINT16 IR; /* Instruction register, with the currently parsed opcode */ - - UINT8 interrupt_pending; /* true if an interrupt must be honored... */ - -#if ! ((TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID)) - UINT8 load_state; /* nonzero if the LOAD* line is active (low) */ -#endif - -#if (TMS99XX_MODEL == TI990_10_ID) || (TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID) - /* On tms9900, we cache the state of INTREQ* and IC0-IC3 here */ - /* On tms9980/9981, we translate the state of IC0-IC2 to the equivalent state for a tms9900, - and store the result here */ - UINT8 irq_level; /* when INTREQ* is active, interrupt level on IC0-IC3 ; else always 16 */ - UINT8 irq_state; /* nonzero if the INTREQ* line is active (low) */ - /* with TMS9940, bit 0 means INT1, bit 1 decrementer, bit 2 INT2 */ -#elif (TMS99XX_MODEL == TMS9995_ID) - /* tms9995 is quite different : it latches the interrupt inputs */ - UINT8 irq_level; /* We store the level of the request with the highest level here */ - UINT8 int_state; /* interrupt lines state */ - UINT8 int_latch; /* interrupt latches state */ -#endif - - /* interrupt callback */ - /* note that this callback is used by tms9900_set_irq_line(cpustate) and tms9980a_set_irq_line(cpustate) to - retreive the value on IC0-IC3 (non-standard behaviour) */ - device_irq_acknowledge_delegate irq_callback; - legacy_cpu_device *device; - address_space *program; - address_space *io; - int icount; - - UINT8 IDLE; /* nonzero if processor is IDLE - i.e waiting for interrupt while writing - special data on CRU bus */ - -#if HAS_MAPPING - UINT8 mapping_on; /* set by a CRU write */ - map_file_t map_files[3]; /* internal mapper registers */ - UINT8 cur_map; /* equivalent to ST_MF status bit */ - UINT8 cur_src_map; /* set to 2 by LDS */ - UINT8 cur_dst_map; /* set to 2 by LDD */ - -#if (TMS99XX_MODEL == TI990_10_ID) - UINT8 reset_maperr; /* reset mapper error flag line (reset flags in 945417-9701 p. 3-90) */ - - UINT32 mapper_address_latch; /* used to load the map file and for diagnostic purpose */ - UINT16 mapper_cru_read_register; /* read register select code for mapper cru interface */ - UINT8 diaglat; /* set when diagnostic address latch is done */ - UINT8 latch_control[3]; /* latch control */ - -#endif -#endif - -#if (TMS99XX_MODEL == TI990_10_ID) - UINT16 error_interrupt_register; /* one flag for each interrupt condition */ - ti99xx_error_interrupt_func error_interrupt_callback; -#endif - -#if (TMS99XX_MODEL == TMS9985_ID) || (TMS99XX_MODEL == TMS9995_ID) - UINT8 RAM[256]; /* on-chip RAM (I know this is weird, but the internal bus is 16-bit-wide, whereas the external bus is 8-bit-wide) */ -#endif - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) || (TMS99XX_MODEL == TMS9995_ID) - /* on-chip event counter/timer*/ - UINT8 decrementer_enabled; - UINT16 decrementer_interval; - UINT16 decrementer_count; /* used in event counter mode*/ - emu_timer *timer; /* used in timer mode */ -#endif - -#if (TMS99XX_MODEL == TMS9995_ID) - /* additionnal registers */ - UINT16 flag; /* flag register */ - UINT8 MID_flag; /* MID flag register */ - - /* chip config, which can be set on reset */ - UINT8 memory_wait_states_byte; - UINT8 memory_wait_states_word; - - /* mask option (off on normal tms9995) */ - UINT8 is_mp9537; -#endif - - /* Some instructions (i.e. XOP, BLWP, and MID) disable interrupt recognition until another - instruction is executed : so they set this flag */ - UINT8 disable_interrupt_recognition; - - /* notify the driver of changes in IDLE state */ - ti99xx_idle_func idle_callback; - -#if EXTERNAL_INSTRUCTION_CALLBACK - ti99xx_rset_func rset_callback; - ti99xx_lrex_func lrex_callback; - ti99xx_ckon_ckof_func ckon_ckof_callback; -#endif - - int write_inhibit; - int extra_byte; /* buffer holding the unused byte in a word read */ -}; - -INLINE tms99xx_state *get_safe_token(device_t *device) -{ - assert(device != NULL); -// assert(device->type() == TMS99XX_GET_INFO); - return (tms99xx_state *)downcast<legacy_cpu_device *>(device)->token(); -} - -#if (TMS99XX_MODEL == TMS9995_ID) -static void reset_decrementer(tms99xx_state *cpustate); -#endif - - -#if (TMS99XX_MODEL == TI990_10_ID) - -/* - accessor for the internal ROM -*/ -READ16_HANDLER(ti990_10_internal_r) -{ - //return cpustate->ROM[offset]; - return space.read_word(0x1ffc00+offset); -} - -#endif - -#if (TMS99XX_MODEL == TMS9995_ID) - -/* - accessor for the first 252 bytes of internal RAM -*/ -READ8_HANDLER(tms9995_internal1_r) -{ - tms99xx_state *cpustate = get_safe_token(&space.device()); - return cpustate->RAM[offset]; -} - -WRITE8_HANDLER(tms9995_internal1_w) -{ - tms99xx_state *cpustate = get_safe_token(&space.device()); - cpustate->RAM[offset]=data; -} - -/* - accessor for the last 4 bytes of internal RAM -*/ -READ8_HANDLER(tms9995_internal2_r) -{ - tms99xx_state *cpustate = get_safe_token(&space.device()); - return cpustate->RAM[offset+0xfc]; -} - -WRITE8_HANDLER(tms9995_internal2_w) -{ - tms99xx_state *cpustate = get_safe_token(&space.device()); - cpustate->RAM[offset+0xfc]=data; -} - -#endif - -#if (TMS99XX_MODEL == TI990_10_ID) - - /* on-board ROMs are not emulated (we use a hack) */ - - #define readword(cs, addr) readwordX((cs), (addr), (cs)->cur_map) - static int readwordX(tms99xx_state *cpustate, int addr, int map_file) - { - if ((map_file == 0) && (addr >= 0xf800)) - { /* intercept TPCS and CPU ROM */ - if (addr < 0xfc00) - /* TPCS */ - return cpustate->program->read_word(0x1f0000+addr); - else - /* CPU ROM */ - return cpustate->program->read_word(0x1f0000+addr); /* hack... */ - } - else if (! cpustate->mapping_on) - { - return cpustate->program->read_word(addr); - } - else - { - int map_index; - - if (addr <= cpustate->map_files[map_file].limit[0]) - map_index = 0; - else if (addr <= cpustate->map_files[map_file].limit[1]) - map_index = 1; - else if (addr <= cpustate->map_files[map_file].limit[2]) - map_index = 2; - else - { /* mapping error */ - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = addr; - if ((! cpustate->reset_maperr) && ! (cpustate->error_interrupt_register & EIR_MAPERR)) - { - cpustate->error_interrupt_register |= EIR_MAPERR; - cpustate->write_inhibit = 1; - } - return cpustate->program->read_word(addr); - } - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = cpustate->map_files[map_file].bias[map_index]+addr; - if ((cpustate->latch_control[map_index]) && (! cpustate->reset_maperr)) - cpustate->diaglat = 1; - return cpustate->program->read_word(cpustate->map_files[map_file].bias[map_index]+addr); - } - } - - #define writeword(cs, addr, data) writewordX((cs), (addr), (data), (cs)->cur_map) - static void writewordX(tms99xx_state *cpustate, int addr, int data, int map_file) - { - if ((map_file == 0) && (addr >= 0xf800)) - { /* intercept TPCS and CPU ROM */ - if (addr < 0xfc00) - /* TPCS */ - cpustate->program->write_word(0x1f0000+addr, data); - else - /* CPU ROM */ - cpustate->program->write_word(0x1f0000+addr, data); /* hack... */ - } - else if (! cpustate->mapping_on) - { - cpustate->program->write_word(addr, data); - } - else - { - int map_index; - - if (addr <= cpustate->map_files[map_file].limit[0]) - map_index = 0; - else if (addr <= cpustate->map_files[map_file].limit[1]) - map_index = 1; - else if (addr <= cpustate->map_files[map_file].limit[2]) - map_index = 2; - else - { /* mapping error */ - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = addr; - if ((! cpustate->reset_maperr) && ! (cpustate->error_interrupt_register & EIR_MAPERR)) - { - cpustate->error_interrupt_register |= EIR_MAPERR; - cpustate->write_inhibit = 1; - } - if (cpustate->write_inhibit) - (void)cpustate->program->read_word(addr); - else - cpustate->program->write_word(addr, data); - return; - } - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = cpustate->map_files[map_file].bias[map_index]+addr; - if ((cpustate->latch_control[map_index]) && (! cpustate->reset_maperr)) - cpustate->diaglat = 1; - cpustate->program->write_word(cpustate->map_files[map_file].bias[map_index]+addr, data); - } - } - - #define readbyte(cs, addr) readbyteX((cs), (addr), (cs)->cur_map) - static int readbyteX(tms99xx_state *cpustate, int addr, int map_file) - { - if ((map_file == 0) && (addr >= 0xf800)) - { /* intercept TPCS and CPU ROM */ - if (addr < 0xfc00) - /* TPCS */ - return cpustate->program->read_byte(0x1f0000+addr); - else - /* CPU ROM */ - return cpustate->program->read_byte(0x1f0000+addr); /* hack... */ - } - else if (! cpustate->mapping_on) - { - return cpustate->program->read_byte(addr); - } - else - { - int map_index; - - if (addr <= cpustate->map_files[map_file].limit[0]) - map_index = 0; - else if (addr <= cpustate->map_files[map_file].limit[1]) - map_index = 1; - else if (addr <= cpustate->map_files[map_file].limit[2]) - map_index = 2; - else - { /* mapping error */ - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = addr; - if ((! cpustate->reset_maperr) && ! (cpustate->error_interrupt_register & EIR_MAPERR)) - { - cpustate->error_interrupt_register |= EIR_MAPERR; - cpustate->write_inhibit = 1; - } - return cpustate->program->read_byte(addr); - } - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = cpustate->map_files[map_file].bias[map_index]+addr; - if ((cpustate->latch_control[map_index]) && (! cpustate->reset_maperr)) - cpustate->diaglat = 1; - return cpustate->program->read_byte(cpustate->map_files[map_file].bias[map_index]+addr); - } - } - - #define writebyte(cs, addr, data) writebyteX((cs), (addr), (data), (cs)->cur_map) - static void writebyteX(tms99xx_state *cpustate, int addr, int data, int map_file) - { - if ((map_file == 0) && (addr >= 0xf800)) - { /* intercept TPCS and CPU ROM */ - if (addr < 0xfc00) - /* TPCS */ - cpustate->program->write_byte(0x1f0000+addr, data); - else - /* CPU ROM */ - cpustate->program->write_byte(0x1f0000+addr, data); /* hack... */ - } - else if (! cpustate->mapping_on) - { - cpustate->program->write_byte(addr, data); - } - else - { - int map_index; - - if (addr <= cpustate->map_files[map_file].limit[0]) - map_index = 0; - else if (addr <= cpustate->map_files[map_file].limit[1]) - map_index = 1; - else if (addr <= cpustate->map_files[map_file].limit[2]) - map_index = 2; - else - { /* mapping error */ - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = addr; - if ((! cpustate->reset_maperr) && ! (cpustate->error_interrupt_register & EIR_MAPERR)) - { - cpustate->error_interrupt_register |= EIR_MAPERR; - cpustate->write_inhibit = 1; - } - if (cpustate->write_inhibit) - (void)cpustate->program->read_byte(addr); - else - cpustate->program->write_byte(addr, data); - return; - } - if ((! (cpustate->error_interrupt_register & EIR_MAPERR)) && ! (cpustate->diaglat)) - cpustate->mapper_address_latch = cpustate->map_files[map_file].bias[map_index]+addr; - if ((cpustate->latch_control[map_index]) && (! cpustate->reset_maperr)) - cpustate->diaglat = 1; - cpustate->program->write_byte(cpustate->map_files[map_file].bias[map_index]+addr, data); - } - } - -#elif (TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9940_ID) - /*16-bit data bus, 16-bit address bus (internal bus in the case of TMS9940)*/ - /*Note that tms9900 actually never accesses a single byte : when performing byte operations, - it reads a 16-bit word, changes the relevant byte, then write a complete word. You should - remember this when writing memory handlers.*/ - /*This does not apply to tms9995 and tms99xxx, but does apply to tms9980 (see below).*/ - - #define readword(cs, addr) (cs)->program->read_word(addr) - #define writeword(cs, addr,data) (cs)->program->write_word((addr), (data)) - - #define readbyte(cs, addr) (cs)->program->read_byte(addr) - #define writebyte(cs, addr,data) (cs)->program->write_byte((addr),(data)) - -#elif (TMS99XX_MODEL == TMS9980_ID) - /*8-bit data bus, 14-bit address*/ - /*Note that tms9980 never accesses a single byte (however crazy it may seem). Although this - makes memory access slower, I have emulated this feature, because if I did otherwise, - there would be some implementation problems in some driver sooner or later.*/ - - INLINE int readword(tms99xx_state *cpustate, int addr) - { - int val; - - cpustate->icount -= 2; - val = cpustate->program->read_byte(addr); - return (val << 8) | cpustate->program->read_byte(addr+1); - } - #define writeword(cs, addr,data) { (cs)->icount -= 2; (cs)->program->write_byte((addr), (data) >> 8); cpustate->program->write_byte((addr) + 1, (data) & 0xff); } - -#if 0 - #define readbyte(cs, addr) ((cs)->icount -= 2, (cs)->program->read_byte(addr)) - #define writebyte(cs, addr,data) { (cs)->icount -= 2; (cs)->program->write_byte((addr),(data)); } -#else - /*This is how it really works*/ - /*Note that every writebyte must match a readbyte (which is indeed the case)*/ - - static int readbyte(tms99xx_state *cpustate, int addr) - { - cpustate->icount -= 2; - if (addr & 1) - { - cpustate->extra_byte = cpustate->program->read_byte(addr-1); - return cpustate->program->read_byte(addr); - } - else - { - int val = cpustate->program->read_byte(addr); - cpustate->extra_byte = cpustate->program->read_byte(addr+1); - return val; - } - } - static void writebyte (tms99xx_state *cpustate, int addr, int data) - { - cpustate->icount -= 2; - if (addr & 1) - { - cpustate->program->write_byte(addr-1, cpustate->extra_byte); - cpustate->program->write_byte(addr, data); - } - else - { - cpustate->program->write_byte(addr, data); - cpustate->program->write_byte(addr+1, cpustate->extra_byte); - } - } -#endif - -#elif (TMS99XX_MODEL == TMS9985_ID) - /*Note that every writebyte must match a readbyte (which is indeed the case)*/ - - static int readword(tms99xx_state *cpustate, int addr) - { - if (addr < 0x2000) - { - } - else if ((addr >= 0x8300) && (addr < 0x8400)) - { - } - else - { - cpustate->icount -= 2; - return (cpustate->program->read_byte(addr) << 8) + cpustate->program->read_byte(addr + 1); - } - } - static void writeword(tms99xx_state *cpustate, int addr, int data) - { - if ((addr >= 0x8300) && (addr < 0x8400)) - { - } - else if (!(addr < 0x2000)) - { - cpustate->icount -= 2; - cpustate->program->write_byte(addr, data >> 8); - cpustate->program->write_byte(addr + 1, data & 0xff); - } - } - - static int readbyte(tms99xx_state *cpustate, int addr) - { - if (addr < 0x2000) - { - } - else if ((addr >= 0x8300) && (addr < 0x8400)) - { - } - else - { - cpustate->icount -= 2; - if (addr & 1) - { - cpustate->extra_byte = cpustate->program->read_byte(addr-1); - return cpustate->program->read_byte(addr); - } - else - { - int val = cpustate->program->read_byte(addr); - cpustate->extra_byte = cpustate->program->read_byte(addr+1); - return val; - } - } - } - static void writebyte(tms99xx_state *cpustate, int addr, int data) - { - if ((addr >= 0x8300) && (addr < 0x8400)) - { - } - else if (!(addr < 0x2000)) - { - cpustate->icount -= 2; - if (addr & 1) - { - cpustate->program->write_byte(addr-1, cpustate->extra_byte); - cpustate->program->write_byte(addr, data); - } - else - { - cpustate->program->write_byte(addr, data); - cpustate->program->write_byte(addr+1, cpustate->extra_byte); - } - } - } - -#elif (TMS99XX_MODEL == TMS9995_ID) - /*8-bit external data bus, with on-chip 16-bit RAM, and 16-bit address bus*/ - /*The code is complex, so we use functions rather than macros*/ - - static TIMER_CALLBACK( decrementer_callback ); - - static int readword(tms99xx_state *cpustate, int addr) - { - if ((addr < 0xf000) || (cpustate->is_mp9537)) - { - int reply; - cpustate->icount -= cpustate->memory_wait_states_word; - reply = cpustate->program->read_byte(addr); - return (reply << 8) | cpustate->program->read_byte(addr + 1); - } - else if (addr < 0xf0fc) - { - return *(UINT16 *)(& cpustate->RAM[addr - 0xf000]); - } - else if (addr < 0xfffa) - { - int reply; - cpustate->icount -= cpustate->memory_wait_states_word; - reply = cpustate->program->read_byte(addr); - return (reply << 8) | cpustate->program->read_byte(addr + 1); - } - else if (addr < 0xfffc) - { - /* read decrementer */ - if (cpustate->decrementer_enabled && !(cpustate->flag & 1)) - /* timer mode, timer enabled */ - return cpustate->device->attotime_to_cycles(cpustate->timer->remaining() / 16); - else - /* event counter mode or timer mode, timer disabled */ - return cpustate->decrementer_count; - } - else - { - return *(UINT16 *)(& cpustate->RAM[addr - 0xff00]); - } - } - - static void writeword (tms99xx_state *cpustate, int addr, int data) - { - if ((addr < 0xf000) || (cpustate->is_mp9537)) - { - cpustate->icount -= cpustate->memory_wait_states_word; - cpustate->program->write_byte(addr, data >> 8); - cpustate->program->write_byte(addr + 1, data & 0xff); - } - else if (addr < 0xf0fc) - { - *(UINT16 *)(& cpustate->RAM[addr - 0xf000]) = data; - } - else if (addr < 0xfffa) - { - cpustate->icount -= cpustate->memory_wait_states_word; - cpustate->program->write_byte(addr, data >> 8); - cpustate->program->write_byte(addr + 1, data & 0xff); - } - else if (addr < 0xfffc) - { - /* write decrementer */ - cpustate->decrementer_interval = data; - reset_decrementer(cpustate); - } - else - { - *(UINT16 *)(& cpustate->RAM[addr - 0xff00]) = data; - } - } - - static int readbyte(tms99xx_state *cpustate, int addr) - { - if ((addr < 0xf000) || (cpustate->is_mp9537)) - { - cpustate->icount -= cpustate->memory_wait_states_byte; - return cpustate->program->read_byte(addr); - } - else if (addr < 0xf0fc) - { - return cpustate->RAM[BYTE_XOR_BE(addr - 0xf000)]; - } - else if (addr < 0xfffa) - { - cpustate->icount -= cpustate->memory_wait_states_byte; - return cpustate->program->read_byte(addr); - } - else if (addr < 0xfffc) - { - /* read decrementer */ - int value; - - if (cpustate->decrementer_enabled && !(cpustate->flag & 1)) - /* timer mode, timer enabled */ - value = cpustate->device->attotime_to_cycles(cpustate->timer->remaining() / 16); - else - /* event counter mode or timer mode, timer disabled */ - value = cpustate->decrementer_count; - - if (addr & 1) - return (value & 0xFF); - else - return (value >> 8); - } - else - { - return cpustate->RAM[BYTE_XOR_BE(addr - 0xff00)]; - } - } - - static void writebyte (tms99xx_state *cpustate, int addr, int data) - { - if ((addr < 0xf000) || (cpustate->is_mp9537)) - { - cpustate->icount -= cpustate->memory_wait_states_byte; - cpustate->program->write_byte(addr, data); - } - else if (addr < 0xf0fc) - { - cpustate->RAM[BYTE_XOR_BE(addr - 0xf000)] = data; - } - else if (addr < 0xfffa) - { - cpustate->icount -= cpustate->memory_wait_states_byte; - cpustate->program->write_byte(addr, data); - } - else if (addr < 0xfffc) - { - /* write decrementer */ - /* Note that a byte write to tms9995 timer messes everything up. */ - cpustate->decrementer_interval = (data << 8) | data; - reset_decrementer(cpustate); - } - else - { - cpustate->RAM[BYTE_XOR_BE(addr - 0xff00)] = data; - } - } - -#else - - #error "memory access not implemented" - -#endif - -#if !HAS_MAPPING - #define readwordX(cs, addr, map_file) readword(cs, addr) - #define writewordX(cs, addr, data, map_file) writeword(cs, (addr), (data)) - #define readbyteX(cs, addr, map_file) readbyte(cs, addr) - #define writebyteX(cs, addr, data, map_file) writebyte(cs, (addr), (data)) -#endif - -#define READREG(reg) readword(cpustate, (cpustate->WP+(reg)) & 0xffff) -#define WRITEREG(reg, data) writeword(cpustate, (cpustate->WP+(reg)) & 0xffff, (data)) - -INLINE UINT16 READREG_DEBUG(tms99xx_state *cpustate, int reg) -{ - int temp = cpustate->icount; - UINT16 result = READREG(reg); - cpustate->icount = temp; - return result; -} - -INLINE void WRITEREG_DEBUG(tms99xx_state *cpustate, int reg, UINT16 data) -{ - int temp = cpustate->icount; - WRITEREG(reg, data); - cpustate->icount = temp; -} - -#if (TMS99XX_MODEL == TI990_10_ID) - READ8_HANDLER(ti990_10_mapper_cru_r) - { - tms99xx_state *cpustate = get_safe_token(&space.device()); - int reply = 0; - - switch(cpustate->mapper_cru_read_register) - { - case 0xb000: - reply = cpustate->map_files[cpustate->cur_map].B[0]; - break; - case 0xb001: - reply = cpustate->map_files[cpustate->cur_map].B[1]; - break; - case 0xb010: - reply = cpustate->map_files[cpustate->cur_map].B[2]; - break; - case 0xb011: - reply = cpustate->map_files[cpustate->cur_map].L[0]; - break; - case 0xb100: - reply = cpustate->map_files[cpustate->cur_map].L[1]; - break; - case 0xb101: - reply = cpustate->map_files[cpustate->cur_map].L[2]; - break; - case 0xb110: - reply = cpustate->mapper_address_latch; - break; - case 0xb111: - reply = cpustate->mapper_address_latch >> 16; - break; - } - - if (offset) - return (reply >> 8) & 0xff; - else - return reply & 0xff; - } - - WRITE8_HANDLER(ti990_10_mapper_cru_w) - { - tms99xx_state *cpustate = get_safe_token(&space.device()); - switch (offset) - { - case 0: - case 1: - case 2: - /* read register select */ - if (data) - cpustate->mapper_cru_read_register |= (1 << offset); - else - cpustate->mapper_cru_read_register &= ~ (1 << offset); - break; - case 3: - /* enable mapping */ - cpustate->mapping_on = data; - break; - case 4: - /* reset flags */ - cpustate->reset_maperr = data; - if (data) - { - cpustate->error_interrupt_register &= ~ EIR_MAPERR; - cpustate->diaglat = 0; - } - break; - case 5: - case 6: - case 7: - /* latch control */ - cpustate->latch_control[7-offset] = data; - break; - } - } - - INLINE void handle_error_interrupt(tms99xx_state *cpustate) - { - if (cpustate->error_interrupt_callback) - (*cpustate->error_interrupt_callback)(cpustate->device, cpustate->error_interrupt_register ? 1 : 0); - } - - READ8_HANDLER(ti990_10_eir_cru_r) - { - tms99xx_state *cpustate = get_safe_token(&space.device()); - return (offset == 1) ? (cpustate->error_interrupt_register & 0xff) : 0; - } - - WRITE8_HANDLER(ti990_10_eir_cru_w) - { - tms99xx_state *cpustate = get_safe_token(&space.device()); - if (offset < 4) /* does not work for EIR_MAPERR */ - { - cpustate->error_interrupt_register &= ~ (1 << offset); - - handle_error_interrupt(cpustate); - } - } - - -#endif - -/* Interrupt mask */ -#define IMASK (cpustate->STATUS & ST_IM) - -/* - CYCLES macro : you provide timings for tms9900 and tms9995, and the macro chooses for you. - - BTW, I have no idea what the timings are for tms9989 and tms99xxx... -*/ -#if TMS99XX_MODEL == TI990_10_ID - /* Use TI990/10 timings*/ - #define CYCLES(a,b,c) cpustate->icount -= a -#elif TMS99XX_MODEL <= TMS9985_ID - /* Use TMS9900/TMS9980 timings*/ - #define CYCLES(a,b,c) cpustate->icount -= b -#else - /* Use TMS9995 timings*/ - #define CYCLES(a,b,c) cpustate->icount -= c*4 -#endif - -#if (TMS99XX_MODEL == TMS9995_ID) - -static void set_flag0(tms99xx_state *cpustate, int val); -static void set_flag1(tms99xx_state *cpustate, int val); - -#endif - -/************************************************************************ - * Status 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; -} - -/**************************************************************************/ - -static void register_for_save_state(device_t *device) -{ - tms99xx_state *cpustate = get_safe_token(device); - device->save_item(NAME(cpustate->WP)); - device->save_item(NAME(cpustate->PC)); - device->save_item(NAME(cpustate->STATUS)); - device->save_item(NAME(cpustate->interrupt_pending)); - -#if ! ((TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID)) - device->save_item(NAME(cpustate->load_state)); -#endif - -#if (TMS99XX_MODEL == TI990_10_ID) || (TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID) - device->save_item(NAME(cpustate->irq_level)); - device->save_item(NAME(cpustate->irq_state)); -#elif (TMS99XX_MODEL == TMS9995_ID) - device->save_item(NAME(cpustate->irq_level)); - device->save_item(NAME(cpustate->int_state)); - device->save_item(NAME(cpustate->int_latch)); -#endif - - device->save_item(NAME(cpustate->IDLE)); - -#if HAS_MAPPING - device->save_item(NAME(cpustate->mapping_on)); - device->save_item(NAME(cpustate->map_files[0].L)); - device->save_item(NAME(cpustate->map_files[0].B)); - device->save_item(NAME(cpustate->map_files[0].limit)); - device->save_item(NAME(cpustate->map_files[0].bias)); - device->save_item(NAME(cpustate->map_files[1].L)); - device->save_item(NAME(cpustate->map_files[1].B)); - device->save_item(NAME(cpustate->map_files[1].limit)); - device->save_item(NAME(cpustate->map_files[1].bias)); - device->save_item(NAME(cpustate->map_files[2].L)); - device->save_item(NAME(cpustate->map_files[2].B)); - device->save_item(NAME(cpustate->map_files[2].limit)); - device->save_item(NAME(cpustate->map_files[2].bias)); - device->save_item(NAME(cpustate->cur_map)); - device->save_item(NAME(cpustate->cur_src_map)); - device->save_item(NAME(cpustate->cur_dst_map)); - -#if (TMS99XX_MODEL == TI990_10_ID) - device->save_item(NAME(cpustate->reset_maperr)); - device->save_item(NAME(cpustate->mapper_address_latch)); - device->save_item(NAME(cpustate->mapper_cru_read_register)); - device->save_item(NAME(cpustate->diaglat)); - device->save_item(NAME(cpustate->latch_control)); -#endif -#endif - -#if (TMS99XX_MODEL == TI990_10_ID) - device->save_item(NAME(cpustate->error_interrupt_register)); -#endif - -#if (TMS99XX_MODEL == TMS9985_ID) || (TMS99XX_MODEL == TMS9995_ID) - device->save_item(NAME(cpustate->RAM)); -#endif - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) || (TMS99XX_MODEL == TMS9995_ID) - device->save_item(NAME(cpustate->decrementer_enabled)); - device->save_item(NAME(cpustate->decrementer_interval)); - device->save_item(NAME(cpustate->decrementer_count)); -#endif - -#if (TMS99XX_MODEL == TMS9995_ID) - device->save_item(NAME(cpustate->flag)); - device->save_item(NAME(cpustate->MID_flag)); - device->save_item(NAME(cpustate->memory_wait_states_byte)); - device->save_item(NAME(cpustate->memory_wait_states_word)); - device->save_item(NAME(cpustate->is_mp9537)); -#endif - - device->save_item(NAME(cpustate->disable_interrupt_recognition)); -} - - -static CPU_INIT( tms99xx ) -{ - const TMS99XX_RESET_PARAM *param = (const TMS99XX_RESET_PARAM *) device->static_config(); - tms99xx_state *cpustate = get_safe_token(device); - - register_for_save_state(device); - - cpustate->irq_level = 16; - cpustate->irq_callback = irqcallback; - cpustate->device = device; - cpustate->program = &device->space(AS_PROGRAM); - cpustate->io = &device->space(AS_IO); - -#if (TMS99XX_MODEL == TMS9995_ID) - cpustate->timer = device->machine().scheduler().timer_alloc(FUNC(decrementer_callback), cpustate); -#endif - - cpustate->idle_callback = param ? param->idle_callback : NULL; - #if (TMS99XX_MODEL == TI990_10_ID) - cpustate->rset_callback = param ? param->rset_callback : NULL; - cpustate->lrex_callback = param ? param->lrex_callback : NULL; - cpustate->ckon_ckof_callback = param ? param->ckon_ckof_callback : NULL; - - cpustate->error_interrupt_callback = param ? param->error_interrupt_callback : NULL; - #endif - -#if (TMS99XX_MODEL == TMS9995_ID) - /* we can ask at reset time that the CPU always generates one wait state automatically */ - if (param == NULL) - { /* if no param, the default is currently "wait state added" */ - cpustate->memory_wait_states_byte = 4; - cpustate->memory_wait_states_word = 12; - cpustate->is_mp9537 = 0; - } - else - { - cpustate->memory_wait_states_byte = (param->auto_wait_state) ? 4 : 0; - cpustate->memory_wait_states_word = (param->auto_wait_state) ? 12 : 4; - cpustate->is_mp9537 = param->is_mp9537; - } -#endif - -} - -/* - TMS9900 hard reset -*/ -static CPU_RESET( tms99xx ) -{ - tms99xx_state *cpustate = get_safe_token(device); - - cpustate->STATUS = 0; /* TMS9980 and TMS9995 Data Books say so */ - getstat(cpustate); - - #if HAS_MAPPING - cpustate->mapping_on = 0; - { - int i,j; - - for (i=0; i<3; i++) - for (j=0; j<3; j++) - { - cpustate->map_files[i].L[j] = 0; - cpustate->map_files[i].limit[j] = 0xffff; - cpustate->map_files[i].B[j] = 0; - cpustate->map_files[i].bias[j] = 0; - } - } - cpustate->cur_map = 0; /* equivalent to ST_MF status bit */ - cpustate->cur_src_map = 0; /* set to 2 by LDS */ - cpustate->cur_dst_map = 0; /* set to 2 by LDD */ - - cpustate->reset_maperr = 0; - #endif - - if (cpustate->IDLE) - { - cpustate->IDLE = 0; /* clear IDLE condition */ - if (cpustate->idle_callback) - (*cpustate->idle_callback)(device, 0); - } - -#if (TMS99XX_MODEL == TMS9995_ID) - cpustate->MID_flag = 0; - - /* Clear flag bits 0 & 1 */ - set_flag0(cpustate, 0); - set_flag1(cpustate, 0); - - /* Clear internal interrupt latches */ - cpustate->int_latch = 0; - cpustate->flag &= 0xFFE3; -#endif - - /* The ST register and interrupt latches changed, didn't they ? */ - field_interrupt(cpustate); - - contextswitchX(cpustate, 0x0000); - - CYCLES(6, 26, 14); -} - -static CPU_EXIT( tms99xx ) -{ - /* nothing to do ? */ -} - -/* fetch : read one word at * PC, and increment PC. */ -INLINE UINT16 fetch(tms99xx_state *cpustate) -{ - UINT16 value = readword(cpustate, cpustate->PC); - cpustate->PC += 2; - return value; -} - - -static CPU_EXECUTE( tms99xx ) -{ - tms99xx_state *cpustate = get_safe_token(device); - - cpustate->lds_flag = 0; - cpustate->ldd_flag = 0; - - do - { - /* all TMS9900 chips I know do not honor interrupts after XOP, BLWP or MID (after any - interrupt-like instruction, actually), and they do not either after LDS and LDD - (There are good reasons for this). */ - if ((cpustate->interrupt_pending) && (! cpustate->disable_interrupt_recognition)) - { - int level; - - level = cpustate->irq_level; - - - if (cpustate->load_state) - { /* LOAD has the highest priority */ - - contextswitchX(cpustate, 0xFFFC); /* load vector, save PC, WP and ST */ - - cpustate->STATUS &= ~ST_IM; /* clear interrupt mask */ - - /* clear IDLE status if necessary */ - if (cpustate->IDLE) - { - cpustate->IDLE = 0; /* clear IDLE condition */ - if (cpustate->idle_callback) - (*cpustate->idle_callback)(device, 0); - } - - CYCLES(6/*to be confirmed*/, 22, 14); - } - else if (level <= IMASK) - { /* a maskable interrupt is honored only if its level isn't greater than IMASK */ - - contextswitchX(cpustate, level*4); /* load vector, save PC, WP and ST */ - - /* change interrupt mask */ - if (level) - { - cpustate->STATUS = (cpustate->STATUS & ~ST_IM) | (level -1); /* decrement mask */ - cpustate->interrupt_pending = 0; /* as a consequence, the interrupt request will be subsequently ignored */ - } - else - cpustate->STATUS &= ~ST_IM; /* clear mask (is this correct???) */ - -#if (TMS99XX_MODEL == TMS9995_ID) - cpustate->STATUS &= 0xFE00; -#endif - - /* clear IDLE status if necessary */ - if (cpustate->IDLE) - { - cpustate->IDLE = 0; /* clear IDLE condition */ - if (cpustate->idle_callback) - (*cpustate->idle_callback)(device, 0); - } - -#if (TMS99XX_MODEL == TMS9995_ID) - /* Clear bit in latch */ - /* I think tms9989 does this, too */ - if (level != 2) - { /* Only do this on level 1, 3, 4 interrupts */ - int mask = 1 << level; - int flag_mask = (level == 1) ? 4 : mask; - - cpustate->int_latch &= ~ mask; - cpustate->flag &= ~ flag_mask; - - /* unlike tms9900, we can call the callback */ - if (level == 1) - (* cpustate->irq_callback)(cpustate->device, 0); - else if (level == 4) - (* cpustate->irq_callback)(cpustate->device, 1); - } -#endif - - CYCLES(6, 22, 14); - } - else - { - logerror("tms9900l.c : the interrupt_pending flag was set incorrectly\n"); - cpustate->interrupt_pending = 0; - } - } - - debugger_instruction_hook(device, cpustate->PC); - - if (cpustate->IDLE) - { /* IDLE instruction has halted execution */ - #if EXTERNAL_INSTRUCTION_DECODING - external_instruction_notify(cpustate, 2); - CYCLES(Moof!, 2, 2); /* 2 cycles per CRU write */ - #else - cpustate->icount = 0; /* much simpler... */ - #endif - } - else - { /* we execute an instruction */ - cpustate->disable_interrupt_recognition = 0; /* default value */ - cpustate->IR = fetch(cpustate); - execute(cpustate, cpustate->IR); - #if HAS_MAPPING - if (cpustate->lds_flag) - { - cpustate->lds_flag = 0; - cpustate->cur_src_map = 2 /*(cpustate->cur_src_map != 2) ? 2 : cpustate->cur_map*/; - } - else - cpustate->cur_src_map = cpustate->cur_map; - if (cpustate->ldd_flag) - { - cpustate->ldd_flag = 0; - cpustate->cur_dst_map = 2 /*(cpustate->cur_src_map != 2) ? 2 : cpustate->cur_map*/; - } - else - cpustate->cur_dst_map = cpustate->cur_map; - #if (TMS99XX_MODEL == TI990_10_ID) - cpustate->write_inhibit = 0; - #endif - #endif - - #if (HAS_OVERFLOW_INTERRUPT) - #if (TMS99XX_MODEL == TMS9995_ID) - /* Note that TI had some problem implementing this... I don't know if this feature works on - a real-world TMS9995. */ - if ((cpustate->STATUS & ST_OVIE) && (cpustate->STATUS & ST_OV) && (cpustate->irq_level > 2)) - cpustate->irq_level = 2; /* interrupt request */ - #else - #warning "todo..." - #endif - #endif - } - - } while (cpustate->icount > 0); -} - -#if (TMS99XX_MODEL == TI990_10_ID) - -static void tms99xx_set_irq_line(tms99xx_state *cpustate, int irqline, int state) -{ - if (irqline == INPUT_LINE_NMI) - { - cpustate->load_state = state; /* save new state */ - - field_interrupt(cpustate); /* interrupt status changed */ - } - else - { - /*if (cpustate->irq_state == state) - return;*/ - - cpustate->irq_state = state; - - if (state == CLEAR_LINE) - cpustate->irq_level = 16; - /* trick : 16 will always be bigger than the IM (0-15), so there will never be interrupts */ - else - cpustate->irq_level = (* cpustate->irq_callback)(cpustate->device, 0); - - field_interrupt(cpustate); /* interrupt state is likely to have changed */ - } -} - -#elif (TMS99XX_MODEL == TMS9900_ID) - -/* -void tms99xx_set_irq_line(cpustate, INPUT_LINE_NMI, int state) : change the state of the LOAD* line - - state == 0 -> LOAD* goes high (inactive) - state != 0 -> LOAD* goes low (active) - - While LOAD* is low, we keep triggering LOAD interrupts... - - A problem : some peripherals lower the LOAD* line for a fixed time interval (causing the 1st - instruction of the LOAD interrupt routine to be repeated while the line is low), and will be - perfectly happy with the current scheme, but others might be more clever and wait for the IAQ - (Instruction acquisition) line to go high, and this needs a callback function to emulate. -*/ - -/* -void tms99xx_set_irq_line(cpustate, int irqline, int state) : sets the state of the interrupt line. - - irqline is ignored, and should always be 0. - - state == 0 -> INTREQ* goes high (inactive) - state != 0 -> INTREQ* goes low (active) -*/ -/* - R Nabet 991020, revised 991218 : - In short : interrupt code should call "cpu_set_irq_line(0, 0, ASSERT_LINE);" to set an - interrupt request (level-triggered interrupts). Also, there MUST be a call to - "cpu_set_irq_line(0, 0, CLEAR_LINE);" in the machine code, when the interrupt line is released by - the hardware (generally in response to an action performed by the interrupt routines). - On tms9995 (9989 ?), you can use PULSE_LINE, too, since the processor latches the line... - - **Note** : HOLD_LINE *NEVER* makes sense on the TMS9900 (or 9980, 9995...). The reason is the - TMS9900 does NOT tell the world it acknoledges an interrupt, so no matter how much hardware you - use, you cannot know when the CPU takes the interrupt, hence you cannot release the line when - the CPU takes the interrupt. Generally, the interrupt condition is cleared by the interrupt - routine (with some CRU or memory access). - - Note that cpu_generate_interrupt uses HOLD_LINE, so your driver interrupt code - should always use the new style, i.e. return "ignore_interrupt(cpustate)" and call - "cpu_set_irq_line(0, 0, ASSERT_LINE);" explicitely. - - Last, many TMS9900-based hardware use a TMS9901 interrupt-handling chip. If anybody wants - to emulate some hardware which uses it, note that I am writing some emulation in the TI99/4(A) - driver in MESS, so you should ask me. -*/ -/* - * HJB 990430: changed to use irq_callback(cpustate) to retrieve the vector - * instead of using 16 irqlines. - * - * R Nabet 990830 : My mistake, I rewrote all these once again ; I think it is now correct. - * A driver using the TMS9900 should do : - * cpu_0_irq_line_vector_w(0, level); - * cpu_set_irq_line(0,0,ASSERT_LINE); - * - * R Nabet 991108 : revised once again, with advice from Juergen Buchmueller, after a discussion - * with Nicola... - * We use the callback to retreive the interrupt level as soon as INTREQ* is asserted. - * As a consequence, I do not support HOLD_LINE normally... However, we do not really have to - * support HOLD_LINE, since no real world TMS9900-based system can support this. - * FYI, there are two alternatives to retreiving the interrupt level with the callback : - * a) using 16 pseudo-IRQ lines. Mostly OK, though it would require a few core changes. - * However, this could cause some problems if someone tried to set two lines simulteanously... - * And TMS9900 did NOT have 16 lines ! This is why Juergen and I did not retain this solution. - * b) modifying the interrupt system in order to provide an extra int to every xxx_set_irq_line - * function. I think this solution would be fine, but it would require quite a number of - * changes in the MAME core. (And I did not feel the courage to check out 4000 drivers and 25 - * cpu cores ;-) .) - * - * Note that this does not apply to tms9995. -*/ -static void tms99xx_set_irq_line(tms99xx_state *cpustate, int irqline, int state) -{ - if (irqline == INPUT_LINE_NMI) - { - cpustate->load_state = state; /* save new state */ - - field_interrupt(cpustate); /* interrupt status changed */ - } - else - { - /*if (cpustate->irq_state == state) - return;*/ - - cpustate->irq_state = state; - - if (state == CLEAR_LINE) - cpustate->irq_level = 16; - /* trick : 16 will always be bigger than the IM (0-15), so there will never be interrupts */ - else - cpustate->irq_level = (* cpustate->irq_callback)(cpustate->device, 0); - - field_interrupt(cpustate); /* interrupt state is likely to have changed */ - } -} - -#elif (TMS99XX_MODEL == TMS9980_ID) -/* - interrupt system similar to tms9900, but only 3 interrupt pins (IC0-IC2) -*/ - -static void tms99xx_set_irq_line(tms99xx_state *cpustate, int irqline, int state) -{ - if (state == CLEAR_LINE) - { - cpustate->load_state = 0; - cpustate->irq_state = 0; - cpustate->irq_level = 16; - /* trick : 16 will always be bigger than the IM (0-15), so there will never be interrupts */ - } - else - { - int level; - - if (irqline == INPUT_LINE_NMI) - level = 2; /* translate MAME's convention to CPU's representation */ - else - level = (* cpustate->irq_callback)(cpustate->device, 0); - - switch (level) - { - case 0: - case 1: - cpustate->load_state = 0; - cpustate->irq_state = 0; - cpustate->irq_level = 16; - CPU_RESET_NAME(tms99xx)(cpustate->device); - break; - case 2: - cpustate->load_state = 1; - cpustate->irq_state = 0; - cpustate->irq_level = 16; - break; - case 7: - cpustate->load_state = 0; - cpustate->irq_state = 0; - cpustate->irq_level = 16; - break; - default: /* external levels 1, 2, 3, 4 */ - cpustate->load_state = 0; - cpustate->irq_state = 1; - cpustate->irq_level = level - 2; - break; - } - } - - field_interrupt(cpustate); /* interrupt state is likely to have changed */ -} - -#elif (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) -/* - 2 interrupt pins (int1 and int2) -*/ - -static void tms99xx_set_irq_line(tms99xx_state *cpustate, int irqline, int state) -{ - int mask; - - if (irqline == 0) - /* INT1 */ - mask = 1; - else if (irqline == 1) - /* INT2 */ - mask = 4; - else - /* What on earth??? */ - return; - - if (state) - cpustate->irq_state |= mask; - else - cpustate->irq_state &= ~mask; - - field_interrupt(cpustate); /* interrupt state is likely to have changed */ -} - -#elif (TMS99XX_MODEL == TMS9995_ID) -/* - this call-back is called by MESS timer system when the timer reaches 0. -*/ -static TIMER_CALLBACK( decrementer_callback ) -{ - tms99xx_state *cpustate = (tms99xx_state *)ptr; - - /* request decrementer interrupt */ - cpustate->int_latch |= 0x8; - cpustate->flag |= 0x8; - - field_interrupt(cpustate); -} - - -/* - reset and load the timer/decrementer - - Note that I don't know whether toggling flag0/flag1 causes the decrementer to be reloaded or not -*/ -static void reset_decrementer(tms99xx_state *cpustate) -{ - cpustate->timer->adjust(attotime::never); - - /* reload count */ - cpustate->decrementer_count = cpustate->decrementer_interval; - - /* decrementer / timer enabled ? */ - cpustate->decrementer_enabled = ((cpustate->flag & 2) && (cpustate->decrementer_interval)); - - if (cpustate->decrementer_enabled && ! (cpustate->flag & 1)) - { /* timer */ - attotime period = cpustate->device->cycles_to_attotime(cpustate->decrementer_interval * 16L); - cpustate->timer->adjust(period, 0, period); - } -} - -/* - You have two interrupt line : one triggers level-1 interrupts, the other triggers level-4 - interrupts (or decrements the decrementer register). - - According to the hardware, you may use PULSE_LINE (edge-triggered interrupts), or ASSERT_LINE - (level-triggered interrupts). Edge-triggered interrupts are way simpler, but if multiple devices - share the same line, they must use level-triggered interrupts. -*/ -static void tms99xx_set_irq_line(tms99xx_state *cpustate, int irqline, int state) -{ - if (irqline == INPUT_LINE_NMI) - { - cpustate->load_state = state; /* save new state */ - - field_interrupt(cpustate); /* interrupt status changed */ - } - else - { - int mask = (irqline == 0) ? 0x2 : 0x10; - int flag_mask = (irqline == 0) ? 0x4 : 0x10; - - if (((cpustate->int_state & mask) != 0) ^ (state != 0)) - { /* only if state changes */ - if (state) - { - cpustate->int_state |= mask; - - if ((irqline == 1) && (cpustate->flag & 1)) - { /* event counter mode : INT4* triggers no interrupt... */ - if (cpustate->decrementer_enabled) - { /* decrement, then interrupt if reach 0 */ - if ((-- cpustate->decrementer_count) == 0) - { - decrementer_callback(cpustate->device->machine(), cpustate, 0); - cpustate->decrementer_count = cpustate->decrementer_interval; /* reload */ - } - } - } - else - { /* plain interrupt mode */ - cpustate->int_latch |= mask; - cpustate->flag |= flag_mask; - } - } - else - { - cpustate->int_state &= ~ mask; - } - - field_interrupt(cpustate); /* interrupt status changed */ - } - } -} - -#else - -#error "interrupt system not implemented" - -#endif - -/* - * field_interrupt - * - * Determines whether if an interrupt is pending, and sets the relevant flag. - * - * Called when an interrupt pin (LOAD*, INTREQ*, IC0-IC3) is changed, and when the interrupt mask - * is modified. - * - * By using this flag, we save some compares in the execution loop. Subtle, isn't it ;-) ? - * - * R Nabet. - */ -#if (TMS99XX_MODEL == TI990_10_ID) || (TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID) - -static void field_interrupt(tms99xx_state *cpustate) -{ - cpustate->interrupt_pending = ((cpustate->irq_level <= IMASK) || (cpustate->load_state)); -} - -#elif (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - -static void field_interrupt(tms99xx_state *cpustate) -{ - if (cpustate->irq_state & 1) - cpustate->irq_level = 1; - else if (cpustate->irq_state & 2) - cpustate->irq_level = 2; - else if (cpustate->irq_state & 4) - cpustate->irq_level = 3; - else - cpustate->irq_level = 4; - /* trick : 4 will always be bigger than the IM (0-3), so there will never be interrupts */ - - cpustate->interrupt_pending = ((cpustate->irq_level <= IMASK) || (cpustate->load_state)); -} - -#elif (TMS99XX_MODEL == TMS9995_ID) - -static void field_interrupt(tms99xx_state *cpustate) -{ - if (cpustate->load_state) - { - cpustate->interrupt_pending = 1; - } - else - { - int current_int; - int level; - - if (cpustate->flag & 1) - /* event counter mode : ignore int4* line... */ - current_int = (cpustate->int_state & ~0x10) | cpustate->int_latch; - else - /* normal behavior */ - current_int = cpustate->int_state | cpustate->int_latch; - - if (current_int) - /* find first bit to 1 */ - /* possible values : 1, 3, 4 */ - for (level=0; ! (current_int & 1); current_int >>= 1, level++) - ; - else - level=16; - - cpustate->irq_level = level; - - cpustate->interrupt_pending = (level <= IMASK); - } -} - -#else - -#error "field_interrupt(cpustate) not written" - -#endif - -static CPU_DISASSEMBLE( tms99xx ) -{ - extern unsigned Dasm9900 (char *buffer, unsigned pc, int model_id, const UINT8 *oprom, const UINT8 *opram); - return Dasm9900(buffer, pc, TMS99XX_MODEL, oprom, opram); -} - - -/*****************************************************************************/ -/* - CRU support code - - The CRU bus is a 1-bit-wide I/O bus. The CPU can read or write bits at random address. - Special instructions are dedicated to reading and writing one or several consecutive bits. - - - - Note that TMS99000 additionally supports parallel CRU operations, although I don't know how - this feature is implemented. -*/ - -enum -{ -#if (TMS99XX_MODEL == TI990_10_ID) - /* 3 MSBs do exist, although they are not connected (don't ask...) */ - CRUAddrBit = 15 -#elif (TMS99XX_MODEL == TMS9900_ID) - /* 3 MSBs are always 0 to support external instructions */ - CRUAddrBit = 12 -#elif (TMS99XX_MODEL == TMS9980_ID) - /* 2 bits unused, and 2 MSBs are always 0 to support external instructions */ - CRUAddrBit = 11 -#elif (TMS99XX_MODEL == TMS9940_ID) - /* 9 internal address lines (8 external) */ - CRUAddrBit = 9 -#elif (TMS99XX_MODEL == TMS9995_ID) - /* no such problem here : data bus lines D0-D2 provide the external instruction code */ - CRUAddrBit = 15 -#else - #warning "I don't know how your processor handles CRU." - CRUAddrBit = 15 -#endif -}; - -enum -{ - rCRUAddrMask = (1 << (CRUAddrBit - 3)) - 1, - wCRUAddrMask = (1 << CRUAddrBit) - 1 -}; - - -#if (TMS99XX_MODEL == TMS9995_ID) - -/* set decrementer mode flag */ -static void set_flag0(tms99xx_state *cpustate, int val) -{ - if (val) - cpustate->flag |= 1; - else - cpustate->flag &= ~ 1; - - reset_decrementer(cpustate); -} - -/* set decrementer enable flag */ -static void set_flag1(tms99xx_state *cpustate, int val) -{ - if (val) - cpustate->flag |= 2; - else - cpustate->flag &= ~ 2; - - reset_decrementer(cpustate); -} - -#endif - -enum cru_error_code -{ - CRU_OK = 0, - CRU_PRIVILEGE_VIOLATION = -1 -}; - -#define WRITEPORT(cs, port, data) (cs)->io->write_byte(port, data) - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) -/* on tms9940, we have to handle internal CRU ports */ -static void write_single_CRU(tms99xx_state *cpustate, int port, int data) -{ - int mask; - - if (! (port & 0x100)) - { - /*if (cpustate->config & CB0)*/ - /* External CRU */ - WRITEPORT(cpustate, port, (data & 0x01)); - } - else - { - /* internal CRU */ - switch ((port >> 4) & 0xf) - { - case 0x8: - if (port == 0x181) - { - /* clear decrementer interrupt */ - cpustate->irq_state &= ~2; - } - if (port >= 0x183) && (port <= 0x186) - { - /* write configuration register */ - mask = 1 << (port - 0x183); - /* ... */ - } - break; - - case 0x9: - if (port <= 0x19D) - { - /* write decrementer latch */ - mask = 1 << (port - 0x190); - /* ... */ - } - else if (port == 0x19E) - { - /* set decrementer as timer (1) or event counter (0) */ - /* ... */ - } - break; - - case 0xA: - /* multiprocessor system interface */ - mask = 1 << (port - 0x1A0); - /* ... */ - break; - - case 0xB: - /* flags */ - mask = 1 << (port - 0x1B0); - /* ... */ - break; - - case 0xC: - case 0xD: - /* direction for P0-P31 */ - mask = 1 << (port - 0x1C0); - /* ... */ - break; - - case 0xE: - case 0xF: - /* data for P0-P31 */ - mask = 1 << (port - 0x1E0); - /* ... */ - break; - } - } -} -#elif (TMS99XX_MODEL == TMS9995_ID) -/* on tms9995, we have to handle internal CRU ports */ -static void write_single_CRU(tms99xx_state *cpustate, int port, int data) -{ - /* Internal CRU */ - switch (port) - { - case 0xF70: - set_flag0(cpustate, data & 0x01); - break; - case 0xF71: - set_flag1(cpustate, data & 0x01); - break; - case 0xF72: - case 0xF73: - case 0xF74: - break; /* ignored */ - case 0xF75: - case 0xF76: - case 0xF77: - case 0xF78: - case 0xF79: - case 0xF7A: - case 0xF7B: - case 0xF7C: - case 0xF7D: - case 0xF7E: - case 0xF7F: - { /* user defined flags */ - int mask = 1 << (port - 0xF70); - if (data & 0x01) - cpustate->flag |= mask; - else - cpustate->flag &= ~ mask; - } - break; - - case 0x0FED: - /* MID flag */ - cpustate->MID_flag = data & 0x01; - break; - } - /* External CRU */ - /* Even though all the registers above are implemented internally, accesses - are passed to the external bus, too, and an external device might respond - to a write to these CRU address as well (particularly a write to the user - flag registers). */ - WRITEPORT(cpustate, port, (data & 0x01)); -} -#else -#define write_single_CRU(cs, port, data) WRITEPORT(cs, port, data) -#endif - -/* - performs a normal write to CRU bus (used by SBZ, SBO, LDCR : address range 0 -> 0xFFF) -*/ -static cru_error_code writeCRU(tms99xx_state *cpustate, int CRUAddr, int Number, UINT16 Value) -{ - int count; - - /*logerror("PC %4.4x Write CRU %x for %x =%x\n",cpustate->PC,CRUAddr,Number,Value);*/ - - CRUAddr &= wCRUAddrMask; - - /* Write Number bits from CRUAddr */ - - for(count=0; count<Number; count++) - { - #if HAS_PRIVILEGE - if ((cpustate->STATUS & ST_PR) && (CRUAddr >= 0xE00)) - return CRU_PRIVILEGE_VIOLATION; - #endif - - write_single_CRU(cpustate, CRUAddr, (Value & 0x01)); - Value >>= 1; - CRUAddr = (CRUAddr + 1) & wCRUAddrMask; - } - - return CRU_OK; -} - -#if EXTERNAL_INSTRUCTION_DECODING -/* - Some opcodes perform a dummy write to a special CRU address, so that an external function may be - triggered. - - Only the first 3 MSBs of the address matter : other address bits and the written value itself - are undefined. - - How should we support this ? With callback functions ? Actually, as long as we do not support - hardware which makes use of this feature, it does not really matter :-) . -*/ -static void external_instruction_notify(tms99xx_state *cpustate, int ext_op_ID) -{ -#if 1 - /* I guess we can support this like normal CRU operations */ -#if (TMS99XX_MODEL == TMS9900_ID) - WRITEPORT(cpustate, ext_op_ID << 12, 0); /* or is it 1 ??? */ -#elif (TMS99XX_MODEL == TMS9980_ID) - WRITEPORT(cpustate, (ext_op_ID & 3) << 11, (ext_op_ID & 4) ? 1 : 0); -#elif (TMS99XX_MODEL == TMS9995_ID) - WRITEPORT(cpustate, ext_op_ID << 15, 0); /* or is it 1 ??? */ -#else - #warning "I don't know how your processor handles external opcodes (maybe you don't need them, though)." -#endif - -#else - switch (ext_op_ID) - { - case 2: /* IDLE */ - - break; - case 3: /* RSET */ - - break; - case 5: /* CKON */ - - break; - case 6: /* CKOF */ - - break; - case 7: /* LREX */ - - break; - case 0: - /* normal CRU write !!! */ - logerror("PC %4.4x : external_instruction_notify : wrong ext_op_ID",cpustate->PC); - break; - default: - /* unknown address */ - logerror("PC %4.4x : external_instruction_notify : unknown ext_op_ID",cpustate->PC); - break; - } -#endif -} -#endif - -/* - performs a normal read to CRU bus (used by TB, STCR : address range 0->0xFFF) - - Note that on some hardware, e.g. TI99/4(a), all normal memory operations cause unwanted CRU - read at the same address. This seems to be impossible to emulate efficiently, so, if you need - to emulate this, you're in trouble. -*/ -#define READPORT(cs, port) (cs)->io->read_byte(port) - - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) -/* on tms9940, we have to handle internal CRU ports */ -static int read_single_CRU(tms99xx_state *cpustate, int port) -{ - int reply; - int shift; - - if (! (port & 0x20)) - { - /*if (cpustate->config & CB0)*/ - /* External CRU */ - reply = READPORT(cpustate, port, (data & 0x01)); - } - else - { - /* internal CRU */ - switch (port) - { - case 0x10: - /* read interrupt state */ - reply = cpustate->irq_state; - break; - - case 0x12: - /* read decrementer LSB */ - /* ... */ - break; - case 0x13: - /* read decrementer MSB */ - /* ... */ - break; - - case 0x14: - /* read multiprocessor system interface LSB */ - /* ... */ - break; - case 0x15: - /* read multiprocessor system interface MSB */ - /* ... */ - break; - - case 0x16: - /* read flags LSB */ - /* ... */ - break; - case 0x17: - /* read flags MSB */ - /* ... */ - break; - - case 0x18: - case 0x19: - case 0x1A: - case 0x1B: - /* direction for P0-P31 */ - shift = (port - 0x18) << 3; - /* ... */ - break; - - case 0x1C: - case 0x1D: - case 0x1E: - case 0x1F: - /* data for P0-P31 */ - shift = (port - 0x1C) << 3; - /* ... */ - break; - - default: - reply = 0; - break; - } - } - - return reply; -} -#elif (TMS99XX_MODEL == TMS9995_ID) -/* on tms9995, we have to handle internal CRU ports */ -static int read_single_CRU(tms99xx_state *cpustate, int port) -{ - switch (port) - { - case 0x1EE: - /* flag, bits 0-7 */ - return cpustate->flag & 0xFF; - case 0x1EF: - /* flag, bits 8-15 */ - return (cpustate->flag >> 8) & 0xFF; - case 0x1FD: - /* MID flag, and external devices */ - if (cpustate->MID_flag) - return READPORT(cpustate, port) | 0x10; - else - return READPORT(cpustate, port) & ~ 0x10; - default: - /* external devices */ - return READPORT(cpustate, port); - } -} -#else -#define read_single_CRU(cs, port) READPORT(cs, port) -#endif - -static int readCRU(tms99xx_state *cpustate, int CRUAddr, int Number) -{ - static const int BitMask[] = - { - 0, /* filler - saves a subtract to find mask */ - 0x0001,0x0003,0x0007,0x000F,0x001F,0x003F,0x007F,0x00FF, - 0x01FF,0x03FF,0x07FF,0x0FFF,0x1FFF,0x3FFF,0x7FFF,0xFFFF - }; - - int Offset,Location,Value; - - /*logerror("Read CRU %x for %x\n",CRUAddr,Number);*/ - - Location = (CRUAddr >> 3) & rCRUAddrMask; - Offset = CRUAddr & 07; - - /* Read 8 bits */ - #if HAS_PRIVILEGE - if ((cpustate->STATUS & ST_PR) && (Location >= (0xE00 >> 3))) - return CRU_PRIVILEGE_VIOLATION; - #endif - Value = read_single_CRU(cpustate, Location); - - if ((Offset+Number) > 8) - { - /* Read next 8 bits */ - Location = (Location + 1) & rCRUAddrMask; - #if HAS_PRIVILEGE - if ((cpustate->STATUS & ST_PR) && (Location >= (0xE00 >> 3))) - return CRU_PRIVILEGE_VIOLATION; - #endif - Value |= read_single_CRU(cpustate, Location) << 8; - - if ((Offset+Number) > 16) - { - /* Read next 8 bits */ - Location = (Location + 1) & rCRUAddrMask; - #if HAS_PRIVILEGE - if ((cpustate->STATUS & ST_PR) && (Location >= (0xE00 >> 3))) - return CRU_PRIVILEGE_VIOLATION; - #endif - Value |= read_single_CRU(cpustate, Location) << 16; - } - } - - /* Allow for Offset */ - Value >>= Offset; - - /* Mask out what we want */ - Value &= BitMask[Number]; - - /* And update */ - return Value; -} - -/*****************************************************************************/ - -#if HAS_MAPPING -/* load a map file from memory */ -static void load_map_file(tms99xx_state *cpustate, UINT16 src_addr, int src_map_file, int dst_file) -{ - int i; - - - /* load mapped address into the memory address register */ - if ((src_map_file == 0) && (src_addr >= 0xf800)) - { /* intercept TPCS and CPU ROM */ - if (src_addr < 0xfc00) - /* TPCS */ - cpustate->mapper_address_latch = 0x1f0000+src_addr; - else - /* CPU ROM */ - cpustate->mapper_address_latch = 0x1f0000+src_addr; /* hack... */ - } - else if (! cpustate->mapping_on) - { - cpustate->mapper_address_latch = src_addr; - } - else - { - int map_index; - - if (src_addr <= cpustate->map_files[src_map_file].limit[0]) - map_index = 0; - else if (src_addr <= cpustate->map_files[src_map_file].limit[1]) - map_index = 1; - else if (src_addr <= cpustate->map_files[src_map_file].limit[2]) - map_index = 2; - else - { - if ((! cpustate->reset_maperr) && ! (cpustate->error_interrupt_register & EIR_MAPERR)) - { - cpustate->error_interrupt_register |= EIR_MAPERR; - cpustate->write_inhibit = 1; - } - cpustate->mapper_address_latch = src_addr; - map_index = -1; - } - if (map_index != -1) - cpustate->mapper_address_latch = cpustate->map_files[src_map_file].bias[map_index]+src_addr; - } - - - for (i=0; i<3; i++) - { - cpustate->map_files[dst_file].L[i] = cpustate->program->read_word(cpustate->mapper_address_latch) & 0xffe0; - cpustate->map_files[dst_file].limit[i] = (cpustate->map_files[dst_file].L[i] ^ 0xffe0) | 0x001f; - cpustate->mapper_address_latch = (cpustate->mapper_address_latch+2) & 0x1fffff; - cpustate->map_files[dst_file].B[i] = cpustate->program->read_word(cpustate->mapper_address_latch); - cpustate->map_files[dst_file].bias[i] = ((unsigned int) cpustate->map_files[dst_file].B[i]) << 5; - cpustate->mapper_address_latch = (cpustate->mapper_address_latch+2) & 0x1fffff; - } -} -#endif - -/* contextswitch : performs a BLWP, i.e. load PC, WP, and save old PC, old WP and ST... */ -static void contextswitch(tms99xx_state *cpustate, UINT16 addr) -{ - UINT16 oldWP, oldpc; - - /* save old state */ - oldWP = cpustate->WP; - oldpc = cpustate->PC; - - /* load vector */ - cpustate->WP = readword(cpustate, addr) & ~1; - cpustate->PC = readword(cpustate, addr+2) & ~1; - - /* write old state to regs */ - WRITEREG(R13, oldWP); - WRITEREG(R14, oldpc); - setstat(cpustate); - WRITEREG(R15, cpustate->STATUS); -} - -#if HAS_MAPPING || HAS_PRIVILEGE - -/* privileged context switch, that occurs after a reset, interrupt or XOP: -we enter privileged mode and select map file 0 before doing the context switch */ -/* For CPU that have no privilege support, contextswitchX would behave -identically to contextswitch, so we can call contextswitch in all cases. */ -static void contextswitchX(tms99xx_state *cpustate, UINT16 addr) -{ - UINT16 oldWP, oldpc, oldST; - - /* save old state */ - oldWP = cpustate->WP; - oldpc = cpustate->PC; - setstat(cpustate); - oldST = cpustate->STATUS; - - /* enter privileged mode and select map file 0 */ - #if HAS_PRIVILEGE - cpustate->STATUS &= ~ ST_PR; - #endif - #if HAS_MAPPING - cpustate->STATUS &= ~ ST_MF; - #endif - getstat(cpustate); - - /* load vector */ - cpustate->WP = readword(cpustate, addr) & ~1; - cpustate->PC = readword(cpustate, addr+2) & ~1; - - /* write old state to regs */ - WRITEREG(R13, oldWP); - WRITEREG(R14, oldpc); - WRITEREG(R15, oldST); -} - -#endif - -/* - * decipheraddr : compute and return the effective address in word instructions. - * - * NOTA : the LSBit is always ignored in word addresses, - * but we do not set it to 0 because of XOP... - */ -static UINT16 decipheraddr(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 ts = opcode & 0x30; - register UINT16 reg = opcode & 0xF; - - reg += reg; - - if (ts == 0) - /* Rx */ - return(reg + cpustate->WP); - else if (ts == 0x10) - { /* *Rx */ - CYCLES(0, 4, 1); - return(readword(cpustate, reg + cpustate->WP)); - } - else if (ts == 0x20) - { - register UINT16 imm; - - imm = fetch(cpustate); - - if (reg) - { /* @>xxxx(Rx) */ - CYCLES(1, 8, 3); - return(readword(cpustate, reg + cpustate->WP) + imm); - } - else - { /* @>xxxx */ - CYCLES(3, 8, 1); - return(imm); - } - } - else /*if (ts == 0x30)*/ - { /* *Rx+ */ - register UINT16 response; - - reg += cpustate->WP; /* reg now contains effective address */ - - CYCLES(1, 8, 3); - - response = readword(cpustate, reg); - writeword(cpustate, reg, response+2); /* we increment register content */ - return(response); - } -} - -/* decipheraddrbyte : compute and return the effective address in byte instructions. */ -static UINT16 decipheraddrbyte(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 ts = opcode & 0x30; - register UINT16 reg = opcode & 0xF; - - reg += reg; - - if (ts == 0) - /* Rx */ - return(reg + cpustate->WP); - else if (ts == 0x10) - { /* *Rx */ - CYCLES(0, 4, 1); - return(readword(cpustate, reg + cpustate->WP)); - } - else if (ts == 0x20) - { - register UINT16 imm; - - imm = fetch(cpustate); - - if (reg) - { /* @>xxxx(Rx) */ - CYCLES(1, 8, 3); - return(readword(cpustate, reg + cpustate->WP) + imm); - } - else - { /* @>xxxx */ - CYCLES(3, 8, 1); - return(imm); - } - } - else /*if (ts == 0x30)*/ - { /* *Rx+ */ - register UINT16 response; - - reg += cpustate->WP; /* reg now contains effective address */ - - CYCLES(1, 6, 3); - - response = readword(cpustate, reg); - writeword(cpustate, reg, response+1); /* we increment register content */ - return(response); - } -} - - -/*************************************************************************/ - -#if (TMS99XX_MODEL == TI990_10_ID) - /* TI990/10 generates an error interrupt */ - /* timings are unknown */ - #define HANDLE_ILLEGAL \ - { \ - cpustate->error_interrupt_register |= EIR_ILLOP; \ - if (cpustate->error_interrupt_callback) \ - (*cpustate->error_interrupt_callback)(cpustate->device, 1); \ - } -#elif TMS99XX_MODEL <= TMS9989_ID - /* TMS9900/TMS9980 merely ignore the instruction */ - #define HANDLE_ILLEGAL cpustate->icount -= 6 -#elif TMS99XX_MODEL == TMS9995_ID - /* TMS9995 generates a MID interrupt */ - #define HANDLE_ILLEGAL \ - { \ - cpustate->MID_flag = 1; \ - contextswitchX(cpustate, 0x0008); \ - cpustate->STATUS = (cpustate->STATUS & 0xFE00) | 0x1; \ - cpustate->disable_interrupt_recognition = 1; \ - } -#else -#define HANDLE_ILLEGAL -#warning "don't know" -#endif - -#if HAS_PRIVILEGE - #if (TMS99XX_MODEL == TI990_10_ID) - /* TI990/10 generates an error interrupt */ - /* timings are unknown */ - #define HANDLE_PRIVILEGE_VIOLATION \ - { \ - cpustate->error_interrupt_register |= EIR_PRIVOP; \ - if (cpustate->error_interrupt_callback) \ - (*cpustate->error_interrupt_callback)(cpustate->device, 1); \ - } - #else - #define HANDLE_PRIVILEGE_VIOLATION - #warning "don't know" - #endif -#endif - -/*========================================================================== - Illegal instructions >0000->01FF (not for 9989 and later) - >0C00->0FFF (not for 990/12 and 99110) -============================================================================*/ - -static void illegal(tms99xx_state *cpustate, UINT16 opcode) -{ - HANDLE_ILLEGAL; -} - - -#if 0 /* ti990/12 only */ -/*========================================================================== - Additionnal instructions, >0000->002F - Additionnal single-register instruction, >0030->003F - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e | - | o p c o d e | reg # | - --------------------------------- - -============================================================================*/ -static void h0000(tms99xx_state *cpustate, UINT16 opcode) -{ - if (opcode >= 0x30) - { /* STPC STore Program Counter */ - - } - else - { - switch (opcode /*& 0x3F*/) - { - case 0x1C: /* SRAM */ - /* SRAM -- Shift Right Arithmetic Multiple precision */ - case 0x1D: /* SLAM */ - /* SLAM -- Shift Left Arithmetic Multiple precision */ - case 0x1E: - /* RTO --- Right Test for Ones */ - case 0x1F: - /* LTO --- Left Test for Ones */ - case 0x20: - /* CNTO -- CouNT Ones */ - case 0x21: - /* SLSL -- Search LiSt Logical address */ - case 0x22: - /* SLSP -- Search LiSt Physical address */ - case 0x23: - /* BDC --- Binary to Decimal ascii Conversion */ - case 0x24: - /* DBC --- Decimal to Binary ascii Conversion */ - case 0x25: - /* SWPM -- SWaP Multiple precision */ - case 0x26: - /* XORM -- eXclusive OR Multiple precision */ - case 0x27: - /* ORM --- OR Multiple precision */ - case 0x28: - /* ANDM -- AND Multiple precision */ - case 0x29: /* SM */ - /* SM ---- Subtract Multiple precision integer */ - case 0x2A: /* AM */ - /* AM ---- Add Multiple precision integer */ - case 0x2B: - /* MOVA -- MOVe Address */ - case 0x2D: - /* EMD --- Execute Micro-Diagnostics */ - case 0x2E: - /* EINT -- Enable INTerrupts */ - case 0x2F: - /* DINT -- Disable INTerrupts */ - - break; - - default: - HANDLE_ILLEGAL; - break; - } - } -} -#endif - - -#if HAS_9995_OPCODES -/*========================================================================== - Additionnal single-register instructions, >0040->00FF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e | reg # | - --------------------------------- - -tms9989 and later : LST, LWP -============================================================================*/ -static void h0040(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 addr; - - addr = opcode & 0xF; - addr = ((addr + addr) + cpustate->WP) & ~1; - - switch ((opcode & 0xF0) >> 4) - { - case 8: /* LST */ - /* LST --- Load STatus register */ - /* ST = *Reg */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - cpustate->STATUS = (cpustate->STATUS & 0x01DF) | (readword(cpustate, addr) & 0xFE20); - else - cpustate->STATUS = readword(cpustate, addr); - #else - cpustate->STATUS = readword(cpustate, addr); - #endif - getstat(cpustate); /* set last_parity */ - break; - - case 9: /* LWP */ - /* LWP --- Load Workspace Pointer */ - /* WP = *Reg */ - cpustate->WP = readword(cpustate, addr) & ~1; - break; - -#if 0 /* 990/12 opcodes */ - case 4: - /* CS ---- Compare Strings */ - case 5: - /* SEQB -- Search string for EQual Byte */ - case 6: - /* MOVS -- MOVe String */ - case 7: - /* LIM --- Load Interrupt Mask */ - - case 10: - /* LCS --- Load writable Control Store */ - - case 11: /* BLSK */ - /* BLSK -- Branch immediate and push Link to StacK */ - - case 12: - /* MVSR -- MoVe String Reverse */ - case 13: - /* MVSK -- MoVe string from StacK */ - case 14: - /* POPS -- POP String from stack */ - case 15: - /* PSHS -- PuSH String to stack */ - - break; -#endif - - default: - HANDLE_ILLEGAL; - break; - } -} - - -/*========================================================================== - Additionnal single-operand instructions, >0100->01FF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e |TS | S | - --------------------------------- - -tms9989 and later : DIVS, MPYS -tms99xxx : BIND -============================================================================*/ -static void h0100(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 src; -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; -#endif - - src = decipheraddr(cpustate, opcode) & ~1; - - switch ((opcode & 0xC0) >> 6) - { -#if HAS_BIND_OPCODE - case 1: /* BIND */ - /* BIND -- Branch INDirect */ - cpustate->PC = readwordX(cpustate, src, src_map) & ~1; - CYCLES(Mooof!, Mooof!, 4 /*don't know*/); - break; -#endif - - case 2: /* DIVS */ - /* DIVS -- DIVide Signed */ - /* R0 = (R0:R1)/S R1 = (R0:R1)%S */ - { - INT16 d = readwordX(cpustate, src, src_map); - INT32 divq = (READREG(R0) << 16) | READREG(R1); - INT32 q = divq/d; - - if ((q < -32768L) || (q > 32767L)) - { - cpustate->STATUS |= ST_OV; - CYCLES(Mooof!, Mooof!, 10); - } - else - { - cpustate->STATUS &= ~ST_OV; - setst_lae(cpustate, q); - WRITEREG(R0, q); - WRITEREG(R1, divq%d); - /* tms9995 : 33 is the worst case */ - CYCLES(Mooof!, Mooof!, 33); - } - } - break; - - case 3: /* MPYS */ - /* MPYS -- MultiPlY Signed */ - /* Results: R0:R1 = R0*S */ - { - INT32 prod = ((INT32) (INT16) readwordX(cpustate, src, src_map)); - prod = prod*((INT32) (INT16) READREG(R0)); - - cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ); - if (prod > 0) - cpustate->STATUS |= (ST_LGT | ST_AGT); - else if (prod < 0) - cpustate->STATUS |= ST_LGT; - else - cpustate->STATUS |= ST_EQ; - - WRITEREG(R0, prod >> 16); - WRITEREG(R1, prod); - } - CYCLES(Mooof!, Mooof!, 25); - break; - -#if 0 /* Origin unknown */ - case 0: /* EVAD */ - /* EVAD -- EValuate ADdress instruction */ - - break; -#endif - - default: - HANDLE_ILLEGAL; - break; - } -} -#endif - - -/*========================================================================== - Immediate, Control instructions, >0200->03FF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e |0| reg # | - --------------------------------- - - LI, AI, ANDI, ORI, CI, STWP, STST, LIMI, LWPI, IDLE, RSET, RTWP, CKON, CKOF, LREX -systems with memory mapper: LMF -============================================================================*/ -static void h0200(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 addr; - register UINT16 value; /* used for anything */ - - addr = opcode & 0xF; - addr = ((addr + addr) + cpustate->WP) & ~1; - - #if HAS_MAPPING - if ((opcode >= 0x0320) && (opcode < 0x0340)) - { /* LMF */ - /* LMF --- Load memory Map File */ - /* Used by the memory mapper on ti990/10 with mapping option, ti990/12, and the TIM99610 - mapper chip to be associated with tms99000. - Syntax: "LMF Rn,m" loads map file m (0 or 1) with six words of memory, starting at address - specified in workspace register Rn (0 through 15). */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - return; - } - #endif - - /* read address pointer */ - addr = readword(cpustate, addr); - - load_map_file(cpustate, addr, cpustate->cur_map, (opcode & 0x10) ? 1 : 0); - - CYCLES(3, Mooof!, Mooof!); - return; - } - #endif - - #if BETTER_0200_DECODING - /* better instruction decoding on ti990/10 */ - if (opcode & 0x10) - { - HANDLE_ILLEGAL; - return; - } - #elif COMPLETE_0200_DECODING - /* full instruction decoding on tms9995 */ - if (((opcode < 0x2E0) && (opcode & 0x10)) || ((opcode >= 0x2E0) && (opcode & 0x1F))) - { -#if 0 - /* tms99110 opcode (not supported by 990/12) */ - if (opcode == 0x0301) - { /* CR ---- Compare Reals */ - } - else - /* tms99105+tms99110 opcode (not supported by 990/12) */ - if (opcode == 0x0302) - { /* MM ---- Multiply Multiple */ - } - else -#endif - #if 0 /* ti990/12 only */ - if (opcode >= 0x03F0) - { /* EP ---- Extended Precision */ - } - else - #endif - HANDLE_ILLEGAL; - return; - } - #endif - - switch ((opcode & 0x1e0) >> 5) - { - case 0: /* LI */ - /* LI ---- Load Immediate */ - /* *Reg = *PC+ */ - value = fetch(cpustate); - writeword(cpustate, addr, value); - setst_lae(cpustate, value); - CYCLES(3, 12, 3); - break; - - case 1: /* AI */ - /* AI ---- Add Immediate */ - /* *Reg += *PC+ */ - value = fetch(cpustate); - value = setst_add_laeco(cpustate, readword(cpustate, addr), value); - writeword(cpustate, addr, value); - CYCLES(4, 14, 4); - break; - - case 2: /* ANDI */ - /* ANDI -- AND Immediate */ - /* *Reg &= *PC+ */ - value = fetch(cpustate); - value = readword(cpustate, addr) & value; - writeword(cpustate, addr, value); - setst_lae(cpustate, value); - CYCLES(4, 14, 4); - break; - - case 3: /* ORI */ - /* ORI --- OR Immediate */ - /* *Reg |= *PC+ */ - value = fetch(cpustate); - value = readword(cpustate, addr) | value; - writeword(cpustate, addr, value); - setst_lae(cpustate, value); - CYCLES(4, 14, 4); - break; - - case 4: /* CI */ - /* CI ---- Compare Immediate */ - /* status = (*Reg-*PC+) */ - value = fetch(cpustate); - setst_c_lae(cpustate, value, readword(cpustate, addr)); - CYCLES(6, 14, 4); - break; - - case 5: /* STWP */ - /* STWP -- STore Workspace Pointer */ - /* *Reg = WP */ - writeword(cpustate, addr, cpustate->WP); - CYCLES(2, 8, 3); - break; - - case 6: /* STST */ - /* STST -- STore STatus register */ - /* *Reg = ST */ - setstat(cpustate); - writeword(cpustate, addr, cpustate->STATUS); - CYCLES(2, 8, 3); - break; - - case 7: /* LWPI */ - /* LWPI -- Load Workspace Pointer Immediate */ - /* WP = *PC+ */ - cpustate->WP = fetch(cpustate) & ~1; - CYCLES(3, 10, 4); - break; - - case 8: /* LIMI */ - /* LIMI -- Load Interrupt Mask Immediate */ - /* ST&15 |= (*PC+)&15 */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - value = fetch(cpustate); - cpustate->STATUS = (cpustate->STATUS & ~ST_IM) | (value & ST_IM); - field_interrupt(cpustate); /*IM has been modified.*/ - CYCLES(3, 16, 5); - break; - - case 9: /* LMF is implemented elsewhere - when it is implemented */ - HANDLE_ILLEGAL; - break; - - case 10: /* IDLE */ - /* IDLE -- IDLE until a reset, interrupt, load */ - /* The TMS99000 locks until an interrupt happen (like with 68k STOP instruction), - and continuously performs a special CRU write (code 2). */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - cpustate->IDLE = 1; - #if EXTERNAL_INSTRUCTION_DECODING - external_instruction_notify(cpustate, 2); - #endif - if (cpustate->idle_callback) - (*cpustate->idle_callback)(cpustate->device, 1); - CYCLES(4, 12, 7); - /* we take care of further external_instruction_notify(cpustate, 2); in execute(cpustate) */ - break; - - case 12: /* RTWP */ - /* RTWP -- Return with Workspace Pointer */ - /* WP = R13, PC = R14, ST = R15 */ - addr = (cpustate->WP + R13) & ~1; - cpustate->WP = readword(cpustate, addr) & ~1; - addr += 2; - cpustate->PC = readword(cpustate, addr) & ~1; - addr += 2; - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - cpustate->STATUS = (cpustate->STATUS & 0x01DF) | (readword(cpustate, addr) & 0xFE20); - else - cpustate->STATUS = readword(cpustate, addr); - #else - cpustate->STATUS = readword(cpustate, addr); - #endif - getstat(cpustate); /* set last_parity */ - - field_interrupt(cpustate); /*IM has been modified.*/ - CYCLES(3, 14, 6); - break; - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - - case 11: /* RSET */ - case 13: /* CKON */ - case 14: /* CKOF */ - case 15: /* LREX */ - HANDLE_ILLEGAL; /* These instruction "have been deleted" on the TMS9940 */ - break; - -#else - - case 11: /* RSET */ - /* RSET -- ReSET */ - /* Reset the Interrupt Mask, and perform a special CRU write (code 3). */ - /* The CRU write is supposed to reset external devices. */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - cpustate->STATUS &= 0xFFF0; /*clear IM.*/ - field_interrupt(cpustate); /*IM has been modified.*/ - - #if (TMS99XX_MODEL == TMS9995_ID) - /*cpustate->MID_flag = 0;*/ /* not sure about this */ - #endif - #if (TMS99XX_MODEL == TI990_10_ID) - cpustate->error_interrupt_register = 0; - cpustate->mapping_on = 0; - - cpustate->reset_maperr = 0; - cpustate->mapper_cru_read_register = 0; - cpustate->latch_control[0] = cpustate->latch_control[1] = cpustate->latch_control[2] = 0; - - cpustate->diaglat = 0; - #endif - - #if EXTERNAL_INSTRUCTION_DECODING - external_instruction_notify(cpustate, 3); - #endif - - #if EXTERNAL_INSTRUCTION_CALLBACK - if (cpustate->rset_callback) - (*cpustate->rset_callback)(cpustate->device); - #endif - - CYCLES(5, 12, 7); - break; - -#if EXTERNAL_INSTRUCTION_DECODING - - case 13: /* CKON */ - case 14: /* CKOF */ - case 15: /* LREX */ - /* CKON -- ClocK ON */ - /* Perform a special CRU write (code 5). */ - /* An external circuitery could, for instance, enable the line clock interrupt (100Hz or 120Hz, depending on the electrical alimentation frequency). */ - /* CKOF -- ClocK OFf */ - /* Perform a special CRU write (code 6). */ - /* An external circuitery could, for instance, disable the line clock interrupt. */ - /* LREX -- Load or REstart eXecution */ - /* Perform a special CRU write (code 7). */ - /* An external circuitery could, for instance, activate the LOAD* line, - causing a non-maskable LOAD interrupt (vector -1). */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - external_instruction_notify(cpustate, (opcode & 0x00e0) >> 5); - - #if EXTERNAL_INSTRUCTION_CALLBACK - #warning "todo..." - #endif - - CYCLES(Mooof!, 12, 7); - break; - -#elif EXTERNAL_INSTRUCTION_CALLBACK - - case 13: /* CKON */ - case 14: /* CKOF */ - /* CKON -- ClocK ON */ - /* Enable the line clock interrupt (100Hz or 120Hz, depending on the electrical alimentation frequency). */ - /* CKOF -- ClocK OFf */ - /* Disable the line clock interrupt. */ - /* We use a callback because the line clock is implemented in machine - code, not in the CPU core. */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - if (cpustate->ckon_ckof_callback) - (*cpustate->ckon_ckof_callback)(cpustate->device, (opcode & 0x0020) ? 1 : 0); - - - CYCLES(5, Mooof!, Mooof!); - break; - - case 15: /* LREX */ - /* LREX -- Load or REstart eXecution */ - /* Trigger a LOAD interrupt (vector -1). (We use a callback, and I - have forgotten the reason why.) */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - if (cpustate->lrex_callback) - (*cpustate->lrex_callback)(cpustate->device); - - CYCLES(6, Mooof!, Mooof!); - break; - -#else - - #warning "Should not happen..." - -#endif - -#endif - } -} - - -/*========================================================================== - Single-operand instructions, >0400->07FF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e |TS | S | - --------------------------------- - - BLWP, B, X, CLR, NEG, INV, INC, INCT, DEC, DECT, BL, SWPB, SETO, ABS -systems with memory mapper: LDD, LDS -============================================================================*/ -static void h0400(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 addr = decipheraddr(cpustate, opcode) & ~1; - register UINT16 value; /* used for anything */ -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; -#endif - - switch ((opcode & 0x3C0) >> 6) - { - case 0: /* BLWP */ - /* BLWP -- Branch and Link with Workspace Pointer */ - /* Result: WP = *S+, PC = *S */ - /* New R13=old WP, New R14=Old PC, New R15=Old ST */ - contextswitch(cpustate, addr); - CYCLES(3, 26, 11); - cpustate->disable_interrupt_recognition = 1; - break; - case 1: /* B */ - /* B ----- Branch */ - /* PC = S */ -#if ((TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID)) - (void) readwordX(cpustate, addr, src_map); -#endif - cpustate->PC = addr; - CYCLES(2, 8, 3); - break; - case 2: /* X */ - /* X ----- eXecute */ - /* Executes instruction *S */ - execute(cpustate, readwordX(cpustate, addr, src_map)); - /* On tms9900, the X instruction actually takes 8 cycles, but we gain 2 cycles on the next - instruction, as we don't need to fetch it. */ - CYCLES(1, 6, 2); - break; - case 3: /* CLR */ - /* CLR --- CLeaR */ - /* *S = 0 */ -#if ((TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID)) - (void) readwordX(cpustate, addr, src_map); -#endif - writewordX(cpustate, addr, 0, src_map); - CYCLES(2, 10, 3); - break; - case 4: /* NEG */ - /* NEG --- NEGate */ - /* *S = -*S */ - value = - (INT16) readwordX(cpustate, addr, src_map); - if (value) - cpustate->STATUS &= ~ ST_C; - else - cpustate->STATUS |= ST_C; - - #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - if (value & 0x0FFF) - cpustate->STATUS &= ~ ST_DC; - else - cpustate->STATUS |= ST_DC; - #endif - - setst_laeo(cpustate, value); - writewordX(cpustate, addr, value, src_map); - CYCLES(3, 12, 3); - break; - case 5: /* INV */ - /* INV --- INVert */ - /* *S = ~*S */ - value = ~ readwordX(cpustate, addr, src_map); - writewordX(cpustate, addr, value, src_map); - setst_lae(cpustate, value); - CYCLES(2, 10, 3); - break; - case 6: /* INC */ - /* INC --- INCrement */ - /* (*S)++ */ - value = setst_add_laeco(cpustate, readwordX(cpustate, addr, src_map), 1); - writewordX(cpustate, addr, value, src_map); - CYCLES(2, 10, 3); - break; - case 7: /* INCT */ - /* INCT -- INCrement by Two */ - /* (*S) +=2 */ - value = setst_add_laeco(cpustate, readwordX(cpustate, addr, src_map), 2); - writewordX(cpustate, addr, value, src_map); - CYCLES(2, 10, 3); - break; - case 8: /* DEC */ - /* DEC --- DECrement */ - /* (*S)-- */ - value = setst_sub_laeco(cpustate, readwordX(cpustate, addr, src_map), 1); - writewordX(cpustate, addr, value, src_map); - CYCLES(2, 10, 3); - break; - case 9: /* DECT */ - /* DECT -- DECrement by Two */ - /* (*S) -= 2 */ - value = setst_sub_laeco(cpustate, readwordX(cpustate, addr, src_map), 2); - writewordX(cpustate, addr, value, src_map); - CYCLES(2, 10, 3); - break; - case 10: /* BL */ - /* BL ---- Branch and Link */ - /* IP=S, R11=old IP */ -#if ((TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID)) - (void) readwordX(cpustate, addr, src_map); -#endif - WRITEREG(R11, cpustate->PC); - cpustate->PC = addr; - CYCLES(3, 12, 5); - break; - case 11: /* SWPB */ - /* SWPB -- SWaP Bytes */ - /* *S = swab(*S) */ - value = readwordX(cpustate, addr, src_map); - value = logical_right_shift(value, 8) | (value << 8); - writewordX(cpustate, addr, value, src_map); - CYCLES(2, 10, 13); - break; - case 12: /* SETO */ - /* SETO -- SET to Ones */ - /* *S = #$FFFF */ -#if ((TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9980_ID)) - (void) readwordX(cpustate, addr, src_map); -#endif - writewordX(cpustate, addr, 0xFFFF, src_map); - CYCLES(2, 10, 3); - break; - case 13: /* ABS */ - /* ABS --- ABSolute value */ - /* *S = |*S| */ - /* clearing ST_C seems to be necessary, although ABS will never set it. */ -#if (TMS99XX_MODEL <= TMS9985_ID) - - /* ti990/10 and tms9900/tms9980 only write the result if it has changed */ - cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV); - - #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - /* I guess ST_DC is cleared here, too*/ - cpustate->STATUS &= ~ ST_DC; - #endif - - value = readwordX(cpustate, addr, src_map); - - CYCLES(5, 12, Mooof!); - - if (((INT16) value) > 0) - cpustate->STATUS |= ST_LGT | ST_AGT; - else if (((INT16) value) < 0) - { - cpustate->STATUS |= ST_LGT; - if (value == 0x8000) - cpustate->STATUS |= ST_OV; - - #if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - if (! (value & 0x0FFF)) - cpustate->STATUS |= ST_DC; - #endif - - writewordX(cpustate, addr, - ((INT16) value), src_map); - CYCLES(0, 2, Mooof!); - } - else - cpustate->STATUS |= ST_EQ; - - break; - -#else - - /* tms9995 always write the result */ - cpustate->STATUS &= ~ (ST_LGT | ST_AGT | ST_EQ | ST_C | ST_OV); - value = readwordX(cpustate, addr, src_map); - - CYCLES(Mooof!, Mooof!, 3); - if (((INT16) value) > 0) - cpustate->STATUS |= ST_LGT | ST_AGT; - else if (((INT16) value) < 0) - { - cpustate->STATUS |= ST_LGT; - if (value == 0x8000) - cpustate->STATUS |= ST_OV; - value = - ((INT16) value); - } - else - cpustate->STATUS |= ST_EQ; - - writewordX(cpustate, addr, value, src_map); - - break; - -#endif - - -#if HAS_MAPPING - /* Used by the memory mapper on ti990/10 with mapping option, ti990/12, and the TIM99610 - mapper chip to be associated with tms99000. */ - /* These opcode allow access to another page without the need of switching a page someplace. */ - /* Note that, if I read the 990/10 schematics correctly, two consecutive LDS or LDD would - cause some trouble. */ - case 14: /* LDS */ - /* LDS --- Long Distance Source */ - - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - load_map_file(cpustate, addr, src_map, 2); - cpustate->lds_flag = 1; - cpustate->disable_interrupt_recognition = 1; - break; - case 15: /* LDD */ - /* LDD --- Long Distance Destination */ - #if HAS_PRIVILEGE - if (cpustate->STATUS & ST_PR) - { - HANDLE_PRIVILEGE_VIOLATION - break; - } - #endif - - load_map_file(cpustate, addr, src_map, 2); - cpustate->ldd_flag = 1; - cpustate->disable_interrupt_recognition = 1; - break; - -#else - - default: - /* illegal instructions */ - HANDLE_ILLEGAL; - break; - -#endif - } -} - - -/*========================================================================== - Shift instructions, >0800->0BFF - -------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e | C | W | - --------------------------------- - - SRA, SRL, SLA, SRC -============================================================================*/ -static void h0800(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 addr; - register UINT16 cnt = (opcode & 0xF0) >> 4; - register UINT16 value; - - addr = (opcode & 0xF); - addr = ((addr+addr) + cpustate->WP) & ~1; - - CYCLES(3, 12, 5); - - if (cnt == 0) - { - CYCLES(2, 8, 2); - - cnt = READREG(R0) & 0xF; - - if (cnt == 0) - cnt = 16; - } - - CYCLES(cnt, cnt+cnt, cnt); - - switch ((opcode & 0x300) >> 8) - { - case 0: /* SRA */ - /* SRA --- Shift Right Arithmetic */ - /* *W >>= C (*W is filled on the left with a copy of the sign bit) */ - value = setst_sra_laec(cpustate, readword(cpustate, addr), cnt); - writeword(cpustate, addr, value); - break; - case 1: /* SRL */ - /* SRL --- Shift Right Logical */ - /* *W >>= C (*W is filled on the left with 0) */ - value = setst_srl_laec(cpustate, readword(cpustate, addr), cnt); - writeword(cpustate, addr, value); - break; - case 2: /* SLA */ - /* SLA --- Shift Left Arithmetic */ - /* *W <<= C */ - value = setst_sla_laeco(cpustate, readword(cpustate, addr), cnt); - writeword(cpustate, addr, value); - break; - case 3: /* SRC */ - /* SRC --- Shift Right Circular */ - /* *W = rightcircularshift(*W, C) */ - value = setst_src_laec(cpustate, readword(cpustate, addr), cnt); - writeword(cpustate, addr, value); - break; - } -} - - -#if 0 /* 990/12 opcodes */ -/*========================================================================== - Additionnal instructions, >0C00->0C0F - Additionnal single-register instructions, >0C10->0C3F - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e | - | o p c o d e | reg # | - --------------------------------- - -============================================================================*/ -static void h0c00(tms99xx_state *cpustate, UINT16 opcode) -{ - if (opcode & 0x30) - { - switch ((opcode & 0x30) >> 4) - { - case 1: - /* INSF -- INSert Field */ - break; - case 2: - /* XV ---- eXtract Value */ - break; - case 3: - /* XF ---- eXtract Field */ - break; - } - } - else - { - switch (opcode & 0x0F) - { - /* floating point instructions */ - case 0: - /* CRI --- Convert Real to Integer */ - break; - case 2: - /* NEGR -- NEGate Real */ - break; - case 4: - /* CRE --- Convert Real to Extended integer */ - break; - case 6: - /* CER --- Convert Extended integer to Real */ - break; - - case 1: - /* CDI --- Convert Double precision real to Integer */ - break; - case 3: - /* NEGD -- NEGate Double precision real */ - break; - case 5: - /* CDE --- Convert Double precision real to Extended integer */ - break; - case 7: - /* CED --- Convert Extended integer to Double precision real */ - break; - - case 8: - /* NRM --- NoRMalize */ - break; - - case 9: - /* TMB --- Test Memory Bit */ - break; - case 10: - /* TCMB -- Test and Clear Memory Bit */ - break; - case 11: - /* TSMB -- Test and Set Memory Bit */ - break; - - case 12: - /* SRJ --- Subtract from Register and Jump */ - case 13: - /* ARJ --- Add to Register and Jump */ - - case 14: - case 15: - /* XIT --- eXIT from floating point interpreter */ - /* Generated by some compilers, but equivalent to NOP on TI990/12. May have been used - by some software floating-point emulators. */ - break; - - default: - HANDLE_ILLEGAL; - break; - } - } -} - - -/*========================================================================== - Additionnal single-operand instructions, >0C40->0DFF - >0E40->0FFF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e |TS | S | - --------------------------------- - -============================================================================*/ -static void h0c40(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 src; - -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; -#endif - - src = decipheraddr(cpustate, opcode) & ~1; - - switch ((opcode & 0x03C0) >> 6) - { - case 1: - /* AR ---- Add Real */ - break; - case 2: - /* CIR --- Convert Integer to Real */ - break; - case 3: - /* SR ---- Subtract Real */ - break; - case 4: - /* MR ---- Multiply Real */ - break; - case 5: - /* DR ---- Divide Real */ - break; - case 6: - /* LR ---- Load Real */ - break; - case 7: - /* STR --- STore Real */ - break; - case 9: - /* AD ---- Add Double */ - break; - case 10: - /* CID --- Convert Integer to Double */ - break; - case 11: - /* SD ---- Subtract Double */ - break; - case 12: - /* MD ---- Multiply Double */ - break; - case 13: - /* DD ---- Divide Double */ - break; - case 14: - /* LD ---- Load Double */ - break; - case 15: - /* SD ---- Store Double */ - break; - default: - HANDLE_ILLEGAL; - break; - } -} - -/*========================================================================== - Additionnal single-register instructions, >0E00->0E3F - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e | reg # | - --------------------------------- - -============================================================================*/ -static void h0e00(tms99xx_state *cpustate, UINT16 opcode) -{ - switch ((opcode & 0x30) >> 4) - { - case 1: - /* IOF --- Invert Order of Field */ - break; - case 1: - /* SNEB -- Search string for Not Equal Byte */ - break; - case 2: - /* CRC --- Cyclic Redundancy Code calculation */ - break; - case 3: - /* TS ---- Translate String */ - break; - } -} -#endif - - -/*========================================================================== - Jump, CRU bit instructions, >1000->1FFF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | o p c o d e | signed offset | - --------------------------------- - - JMP, JLT, JLE, JEQ, JHE, JGT, JNE, JNC, JOC, JNO, JL, JH, JOP - SBO, SBZ, TB -============================================================================*/ -static void h1000(tms99xx_state *cpustate, UINT16 opcode) -{ - /* we convert 8 bit signed word offset to a 16 bit effective word offset. */ - register INT16 offset = ((INT8) opcode); - - - switch ((opcode & 0xF00) >> 8) - { - case 0: /* JMP */ - /* JMP --- unconditional JuMP */ - /* PC += offset */ - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - break; - case 1: /* JLT */ - /* JLT --- Jump if Less Than (arithmetic) */ - /* if (A==0 && EQ==0), PC += offset */ - if (! (cpustate->STATUS & (ST_AGT | ST_EQ))) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 2: /* JLE */ - /* JLE --- Jump if Lower or Equal (logical) */ - /* if (L==0 || EQ==1), PC += offset */ - if ((! (cpustate->STATUS & ST_LGT)) || (cpustate->STATUS & ST_EQ)) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 3: /* JEQ */ - /* JEQ --- Jump if EQual */ - /* if (EQ==1), PC += offset */ - if (cpustate->STATUS & ST_EQ) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 4: /* JHE */ - /* JHE --- Jump if Higher or Equal (logical) */ - /* if (L==1 || EQ==1), PC += offset */ - if (cpustate->STATUS & (ST_LGT | ST_EQ)) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 5: /* JGT */ - /* JGT --- Jump if Greater Than (arithmetic) */ - /* if (A==1), PC += offset */ - if (cpustate->STATUS & ST_AGT) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 6: /* JNE */ - /* JNE --- Jump if Not Equal */ - /* if (EQ==0), PC += offset */ - if (! (cpustate->STATUS & ST_EQ)) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 7: /* JNC */ - /* JNC --- Jump if No Carry */ - /* if (C==0), PC += offset */ - if (! (cpustate->STATUS & ST_C)) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 8: /* JOC */ - /* JOC --- Jump On Carry */ - /* if (C==1), PC += offset */ - if (cpustate->STATUS & ST_C) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 9: /* JNO */ - /* JNO --- Jump if No Overflow */ - /* if (OV==0), PC += offset */ - if (! (cpustate->STATUS & ST_OV)) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 10: /* JL */ - /* JL ---- Jump if Lower (logical) */ - /* if (L==0 && EQ==0), PC += offset */ - if (! (cpustate->STATUS & (ST_LGT | ST_EQ))) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 11: /* JH */ - /* JH ---- Jump if Higher (logical) */ - /* if (L==1 && EQ==0), PC += offset */ - if ((cpustate->STATUS & ST_LGT) && ! (cpustate->STATUS & ST_EQ)) - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - break; - case 12: /* JOP */ - /* JOP --- Jump On (odd) Parity */ - /* if (P==1), PC += offset */ - { - /* Let's set ST_OP. */ - int i; - UINT8 a; - a = cpustate->lastparity; - i = 0; - - while (a != 0) - { - if (a & 1) /* If current bit is set, */ - i++; /* increment bit count. */ - a >>= 1U; /* Next bit. */ - } - - /* Set ST_OP bit. */ - /*if (i & 1) - cpustate->STATUS |= ST_OP; - else - cpustate->STATUS &= ~ ST_OP;*/ - - /* Jump accordingly. */ - if (i & 1) /*(cpustate->STATUS & ST_OP)*/ - { - cpustate->PC += (offset + offset); - CYCLES(3, 10, 3); - } - else - CYCLES(2, 8, 3); - } - - break; - case 13: /* SBO */ - /* SBO --- Set Bit to One */ - /* CRU Bit = 1 */ - #if HAS_PRIVILEGE - if (writeCRU(cpustate, (READREG(R12) >> 1) + offset, 1, 1) == CRU_PRIVILEGE_VIOLATION) - HANDLE_PRIVILEGE_VIOLATION - #else - writeCRU(cpustate, (READREG(R12) >> 1) + offset, 1, 1); - #endif - - CYCLES(4, 12, 8); - break; - - case 14: /* SBZ */ - /* SBZ --- Set Bit to Zero */ - /* CRU Bit = 0 */ - #if HAS_PRIVILEGE - if (writeCRU(cpustate, (READREG(R12) >> 1) + offset, 1, 0) == CRU_PRIVILEGE_VIOLATION) - HANDLE_PRIVILEGE_VIOLATION - #else - writeCRU(cpustate, (READREG(R12) >> 1) + offset, 1, 0); - #endif - - CYCLES(4, 12, 8); - break; - - case 15: /* TB */ - /* TB ---- Test Bit */ - /* EQ = (CRU Bit == 1) */ - #if HAS_PRIVILEGE - { - int value; - - value = readCRU(cpustate, (READREG(R12)>> 1) + offset, 1); - if (value == CRU_PRIVILEGE_VIOLATION) - HANDLE_PRIVILEGE_VIOLATION - else - setst_e(cpustate, value & 1, 1); - } - #else - setst_e(cpustate, readCRU(cpustate, (READREG(R12)>> 1) + offset, 1) & 1, 1); - #endif - CYCLES(5, 12, 8); - break; - } -} - - -/*========================================================================== - General and One-Register instructions >2000->3FFF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - --------------------------------- - | opcode | D |TS | S | - --------------------------------- - - COC, CZC, XOR, LDCR, STCR, XOP, MPY, DIV -tms9940 : DCA, DCS, LIIM -==========================================================================*/ - -/* xop, ldcr and stcr are handled elsewhere */ -static void h2000(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 dest = (opcode & 0x3C0) >> 6; - register UINT16 src; - register UINT16 value; - -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; -#endif - - src = decipheraddr(cpustate, opcode) & ~1; - dest = ((dest+dest) + cpustate->WP) & ~1; - - switch ((opcode & 0x1C00) >> 10) - { - case 0: /* COC */ - /* COC --- Compare Ones Corresponding */ - /* status E bit = (S&D == S) */ - value = readwordX(cpustate, src, src_map); - setst_e(cpustate, value & readword(cpustate, dest), value); - CYCLES(5, 14, 4); - break; - case 1: /* CZC */ - /* CZC --- Compare Zeroes Corresponding */ - /* status E bit = (S&~D == S) */ - value = readwordX(cpustate, src, src_map); - setst_e(cpustate, value & (~ readword(cpustate, dest)), value); - CYCLES(5, 14, 4); - break; - case 2: /* XOR */ - /* XOR --- eXclusive OR */ - /* D ^= S */ - value = readwordX(cpustate, src, src_map); - value ^= readword(cpustate, dest); - setst_lae(cpustate, value); - writeword(cpustate, dest,value); - CYCLES(3, 14, 4); - break; - /*case 3:*/ /* XOP is implemented elsewhere */ - /*case 4:*/ /* LDCR is implemented elsewhere */ - /*case 5:*/ /* STCR is implemented elsewhere */ - case 6: /* MPY */ - /* MPY --- MultiPlY (unsigned) */ - /* Results: D:D+1 = D*S */ - /* Note that early TMS9995 reportedly performs an extra dummy read in PC space */ - { - unsigned long prod = ((unsigned long) readwordX(cpustate, src, src_map)); - prod = prod * ((unsigned long) readword(cpustate, dest)); - writeword(cpustate, dest, prod >> 16); - writeword(cpustate, (dest+2)&0xffff, prod); - } - /* ti990/10 : from 19 to 35, possibly 19 + (number of bits to 1 in one operand) */ - CYCLES(35, 52, 23); - break; - case 7: /* DIV */ - /* DIV --- DIVide (unsigned) */ - /* D = D/S D+1 = D%S */ - { - UINT16 d = readwordX(cpustate, src, src_map); - UINT16 hi = readword(cpustate, dest); - unsigned long divq = (((unsigned long) hi) << 16) | readword(cpustate, (dest+2)&0xffff); - - if (d <= hi) - { - cpustate->STATUS |= ST_OV; - CYCLES(4, 16, 6); - } - else - { - cpustate->STATUS &= ~ST_OV; - writeword(cpustate, dest, divq/d); - writeword(cpustate, (dest+2)&0xffff, divq%d); - /* tms9900 : from 92 to 124, possibly 92 + 2*(number of bits to 1 (or 0?) in quotient) */ - /* tms9995 : 28 is the worst case */ - /* ti990/10 : from 41 to 58, possibly 41 + (number of bits to 1 (or 0?) in quotient) */ - CYCLES(41, 92, 28); - } - } - break; - } -} - -static void xop(tms99xx_state *cpustate, UINT16 opcode) -{ /* XOP */ - /* XOP --- eXtended OPeration */ - /* WP = *(40h+D), PC = *(42h+D) */ - /* New R13=old WP, New R14=Old IP, New R15=Old ST */ - /* New R11=S */ - /* Xop bit set */ - - register UINT16 immediate = (opcode & 0x3C0) >> 6; - register UINT16 operand; - - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - switch (immediate) - { - case 0: /* DCA */ - /* DCA --- Decimal Correct Addition */ - operand = decipheraddrbyte(cpustate, opcode); - { - int value = readbyte(cpustate, operand); - int X = (value >> 4) & 0xf; - int Y = value & 0xf; - - if (Y >= 10) - { - Y -= 10; - cpustate->STATUS |= ST_DC; - X++; - } - else if (cpustate->STATUS & ST_DC) - { - Y += 6; - } - - if (X >= 10) - { - X -= 10; - cpustate->STATUS |= ST_C; - } - else if (cpustate->STATUS & ST_C) - { - X += 6; - } - - writebyte(cpustate, operand, (X << 4) | Y); - } - break; - case 1: /* DCS */ - /* DCS --- Decimal Correct Substraction */ - operand = decipheraddrbyte(cpustate, opcode); - { - int value = readbyte(cpustate, operand); - - if (! (cpustate->STATUS & ST_DC)) - { - value += 10; - } - - if (! (cpustate->STATUS & ST_C)) - { - value += 10 << 4; - } - - cpustate->STATUS ^= ST_DC; - - writebyte(cpustate, operand, value); - } - break; - case 2: /* LIIM */ - case 3: /* LIIM */ - /* LIIM - Load Immediate Interrupt Mask */ - /* Does the same job as LIMI, with a different opcode format. */ - /* Note that, unlike TMS9900, the interrupt mask is only 2-bit long. */ - (void)decipheraddr(cpustate, opcode); /* dummy decode (personnal guess) */ - - cpustate->STATUS = (cpustate->STATUS & ~ST_IM) | (opcode & ST_IM); - break; - default: /* normal XOP */ -#endif - - /* TODO : emulate 990/10 hardware XOP */ - operand = decipheraddr(cpustate, opcode); - - #if ((TMS99XX_MODEL <= TMS9989_ID) && (TMS99XX_MODEL != TI990_10_ID)) - (void)readword(cpustate, operand & ~1); /*dummy read (personnal guess)*/ - #endif - - contextswitchX(cpustate, 0x40 + (immediate << 2)); - - #if ! ((TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID)) - /* The bit is not set on tms9940 */ - cpustate->STATUS |= ST_X; - #endif - - WRITEREG(R11, operand); - CYCLES(7, 36, 15); - cpustate->disable_interrupt_recognition = 1; - -#if (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) - break; - } -#endif -} - -/* LDCR and STCR */ -static void ldcr_stcr(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 cnt = (opcode & 0x3C0) >> 6; - register UINT16 addr; - int value; - -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; -#endif - - if (cnt == 0) - cnt = 16; - - if (cnt <= 8) - addr = decipheraddrbyte(cpustate, opcode); - else - addr = decipheraddr(cpustate, opcode) & ~1; - - if (opcode < 0x3400) - { /* LDCR */ - /* LDCR -- LoaD into CRu */ - /* CRU R12--CRU R12+D-1 set to S */ - if (cnt <= 8) - { -#if (TMS99XX_MODEL != TMS9995_ID) - value = readbyteX(cpustate, addr, src_map); -#else - /* just for once, tms9995 behaves like earlier 8-bit tms99xx chips */ - /* this must be because instruction decoding is too complex */ - value = readwordX(cpustate, addr & ~1, src_map); - if (addr & 1) - value &= 0xFF; - else - value = (value >> 8) & 0xFF; -#endif - - (void)READREG(cnt+cnt); /*dummy read (reasonnable guess for TMS9995 & TMS9900, ti990/10)*/ - - setst_byte_laep(cpustate, value); - } - else - { - value = readwordX(cpustate, addr, src_map); - - (void)READREG(cnt+cnt); /*dummy read (reasonnable guess for TMS9995 & TMS9900, ti990/10)*/ - - setst_lae(cpustate, value); - } - - #if HAS_PRIVILEGE - if (writeCRU(cpustate, (READREG(R12) >> 1), cnt, value) == CRU_PRIVILEGE_VIOLATION) - HANDLE_PRIVILEGE_VIOLATION - #else - writeCRU(cpustate, (READREG(R12) >> 1), cnt, value); - #endif - - CYCLES(4+cnt, 20 + cnt+cnt, 9 + cnt+cnt); - } - else - { /* STCR */ - /* STCR -- STore from CRu */ - /* S = CRU R12--CRU R12+D-1 */ - if (cnt <= 8) - { -#if (TMS99XX_MODEL != TMS9995_ID) - - (void)READREG(cnt+cnt); /*dummy read (reasonable guess for TMS9995 & TMS9900, ti990/10)*/ - // MZ: Read before write - int value2 = readwordX(cpustate, addr & ~1, src_map); - - #if HAS_PRIVILEGE - value = readCRU(cpustate, (READREG(R12) >> 1), cnt); - - if (value == CRU_PRIVILEGE_VIOLATION) - HANDLE_PRIVILEGE_VIOLATION - else - { - setst_byte_laep(cpustate, value); - writewordX(cpustate, addr, ((value << 8) & 0xff00) | (value2 & 0x00ff), src_map); - } - #else - value = readCRU(cpustate, (READREG(R12) >> 1), cnt); - setst_byte_laep(cpustate, value); - - writewordX(cpustate, addr, ((value << 8) & 0xff00) | (value2 & 0x00ff), src_map); - #endif - CYCLES(18+cnt, (cnt != 8) ? 42 : 44, 19 + cnt); -#else - /* just for once, tms9995 behaves like earlier 8-bit tms99xx chips */ - /* this must be because instruction decoding is too complex */ - int value2 = readwordX(cpustate, addr & ~1, src_map); - - (void)READREG(cnt+cnt); /*dummy read (reasonable guess for TMS9995 & TMS9900, ti990/10)*/ - - value = readCRU(cpustate, (READREG(R12) >> 1), cnt); - setst_byte_laep(cpustate, value); - - if (addr & 1) - writewordX(cpustate, addr & ~1, (value & 0x00FF) | (value2 & 0xFF00), src_map); - else - writewordX(cpustate, addr & ~1, (value2 & 0x00FF) | ((value << 8) & 0xFF00), src_map); - - CYCLES(Mooof!, Mooof!, 19 + cnt); -#endif - } - else - { - (void)readwordX(cpustate, addr, src_map); /*dummy read*/ - - (void)READREG(cnt+cnt); /*dummy read (reasonnable guess for TMS9995 & TMS9900, ti990/10)*/ - - #if HAS_PRIVILEGE - value = readCRU(cpustate, (READREG(R12) >> 1), cnt); - if (value == CRU_PRIVILEGE_VIOLATION) - HANDLE_PRIVILEGE_VIOLATION - else - { - setst_lae(cpustate, value); - writewordX(cpustate, addr, value, src_map); - } - #else - value = readCRU(cpustate, (READREG(R12) >> 1), cnt); - setst_lae(cpustate, value); - writewordX(cpustate, addr, value, src_map); - #endif - CYCLES(24+cnt, (cnt != 16) ? 58 : 60, 27 + cnt); - } - } -} - - -/*========================================================================== - Two-Operand instructions >4000->FFFF - --------------------------------------------------------------------------- - - 0 1 2 3-4 5 6 7+8 9 A B-C D E F - ---------------------------------- - |opcode|B|TD | D |TS | S | - ---------------------------------- - - SZC, SZCB, S, SB, C, CB, A, AB, MOV, MOVB, SOC, SOCB -============================================================================*/ - -/* word instructions */ -static void h4000w(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 src; - register UINT16 dest; - register UINT16 value; - -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; - int dst_map = (opcode & 0x0c00) ? cpustate->cur_dst_map : cpustate->cur_map; -#endif - - src = decipheraddr(cpustate, opcode) & ~1; - dest = decipheraddr(cpustate, opcode >> 6) & ~1; - - value = readwordX(cpustate, src, src_map); - - switch ((opcode >> 13) & 0x0007) /* ((opcode & 0xE000) >> 13) */ - { - case 2: /* SZC */ - /* SZC --- Set Zeros Corresponding */ - /* D &= ~S */ - value = readwordX(cpustate, dest, dst_map) & (~ value); - setst_lae(cpustate, value); - writewordX(cpustate, dest, value, dst_map); - CYCLES(2, 14, 4); - break; - case 3: /* S */ - /* S ----- Subtract */ - /* D -= S */ - value = setst_sub_laeco(cpustate, readwordX(cpustate, dest, dst_map), value); - writewordX(cpustate, dest, value, dst_map); - CYCLES(2, 14, 4); - break; - case 4: /* C */ - /* C ----- Compare */ - /* ST = (D - S) */ - setst_c_lae(cpustate, readwordX(cpustate, dest, dst_map), value); - CYCLES(5, 14, 4); - break; - case 5: /* A */ - /* A ----- Add */ - /* D += S */ - value = setst_add_laeco(cpustate, readwordX(cpustate, dest, dst_map), value); - writewordX(cpustate, dest, value, dst_map); - CYCLES(2, 14, 4); - break; - case 6: /* MOV */ - /* MOV --- MOVe */ - /* D = S */ - setst_lae(cpustate, value); - #if ((TMS99XX_MODEL >= TMS9900_ID) && (TMS99XX_MODEL <= TMS9985_ID)) - /* MOV performs a dummy read with tms9900/9980 (but neither ti990/10 nor tms9995) */ - (void)readwordX(cpustate, dest, dst_map); - #endif - writewordX(cpustate, dest, value, dst_map); - CYCLES(1, 14, 3); - break; - case 7: /* SOC */ - /* SOC --- Set Ones Corresponding */ - /* D |= S */ - value = value | readwordX(cpustate, dest, dst_map); - setst_lae(cpustate, value); - writewordX(cpustate, dest, value, dst_map); - CYCLES(2, 14, 4); - break; - } -} - -/* byte instruction */ -static void h4000b(tms99xx_state *cpustate, UINT16 opcode) -{ - register UINT16 src; - register UINT16 dest; - register UINT16 value; - -#if HAS_MAPPING - int src_map = (opcode & 0x0030) ? cpustate->cur_src_map : cpustate->cur_map; - int dst_map = (opcode & 0x0c00) ? cpustate->cur_dst_map : cpustate->cur_map; -#endif - - src = decipheraddrbyte(cpustate, opcode); - dest = decipheraddrbyte(cpustate, opcode >> 6); - - value = readbyteX(cpustate, src, src_map); - - switch ((opcode >> 13) & 0x0007) /* ((opcode & 0xE000) >> 13) */ - { - case 2: /* SZCB */ - /* SZCB -- Set Zeros Corresponding, Byte */ - /* D &= ~S */ - value = readbyteX(cpustate, dest, dst_map) & (~ value); - setst_byte_laep(cpustate, value); - writebyteX(cpustate, dest, value, dst_map); - CYCLES(3, 14, 4); - break; - case 3: /* SB */ - /* SB ---- Subtract, Byte */ - /* D -= S */ - value = setst_subbyte_laecop(cpustate, readbyteX(cpustate, dest, dst_map), value); - writebyteX(cpustate, dest, value, dst_map); - CYCLES(3, 14, 4); - break; - case 4: /* CB */ - /* CB ---- Compare Bytes */ - /* ST = (D - S) */ - setst_c_lae(cpustate, readbyteX(cpustate, dest, dst_map)<<8, value<<8); - cpustate->lastparity = value; - CYCLES(5, 14, 4); - break; - case 5: /* AB */ - /* AB ---- Add, Byte */ - /* D += S */ - value = setst_addbyte_laecop(cpustate, readbyteX(cpustate, dest, dst_map), value); - writebyteX(cpustate, dest, value, dst_map); - CYCLES(3, 14, 4); - break; - case 6: /* MOVB */ - /* MOVB -- MOVe Bytes */ - /* D = S */ - setst_byte_laep(cpustate, value); - #if (TMS99XX_MODEL <= TMS9985_ID) - /* On ti990/10 and tms9900, MOVB needs to read destination, because it cannot actually - read one single byte. It reads a word, replaces the relevant byte, then write - the result. A tms9980 should not need to do so, but still does, because it is just - a tms9900 with a 16 to 8 bit multiplexer (instead of a new chip design, like tms9995). */ - (void)readbyteX(cpustate, dest, dst_map); - #endif - writebyteX(cpustate, dest, value, dst_map); - CYCLES(3, 14, 3); - break; - case 7: /* SOCB */ - /* SOCB -- Set Ones Corresponding, Byte */ - /* D |= S */ - value = value | readbyteX(cpustate, dest, dst_map); - setst_byte_laep(cpustate, value); - writebyteX(cpustate, dest, value, dst_map); - CYCLES(3, 14, 4); - break; - } -} - - -INLINE void execute(tms99xx_state *cpustate, UINT16 opcode) -{ -#if (! HAS_9995_OPCODES) - - /* tms9900-like instruction set*/ - - static void (*const jumptable_short[128])(tms99xx_state *,UINT16) = - { - &illegal,&h0200,&h0400,&h0400,&h0800,&h0800,&illegal,&illegal, - &h1000,&h1000,&h1000,&h1000,&h1000,&h1000,&h1000,&h1000, - &h2000,&h2000,&h2000,&h2000,&h2000,&h2000,&xop,&xop, - &ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&h2000,&h2000,&h2000,&h2000, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b - }; - - (* jumptable_short[opcode >> 9])(cpustate, opcode); - -#else - - /* tms9989 and tms9995 include 4 extra instructions, and one additionnal instruction type */ - /* tms99000 includes yet another additional instruction */ - - static void (*const jumptable_long[256])(tms99xx_state *,UINT16) = - { - &h0040,&h0100,&h0200,&h0200,&h0400,&h0400,&h0400,&h0400, - &h0800,&h0800,&h0800,&h0800,&illegal,&illegal,&illegal,&illegal, - &h1000,&h1000,&h1000,&h1000,&h1000,&h1000,&h1000,&h1000, - &h1000,&h1000,&h1000,&h1000,&h1000,&h1000,&h1000,&h1000, - &h2000,&h2000,&h2000,&h2000,&h2000,&h2000,&h2000,&h2000, - &h2000,&h2000,&h2000,&h2000,&xop,&xop,&xop,&xop, - &ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&ldcr_stcr,&ldcr_stcr, - &h2000,&h2000,&h2000,&h2000,&h2000,&h2000,&h2000,&h2000, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w,&h4000w, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b, - &h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b,&h4000b - }; - - (* jumptable_long[opcode >> 8])(cpustate, opcode); - -#endif -} - -/************************************************************************** - * Generic set_info - **************************************************************************/ - -static CPU_SET_INFO( tms99xx ) -{ - tms99xx_state *cpustate = get_safe_token(device); - switch (state) - { - /* --- the following bits of info are set as 64-bit signed integers --- */ - case CPUINFO_INT_INPUT_STATE + INPUT_LINE_NMI: tms99xx_set_irq_line(cpustate, INPUT_LINE_NMI, info->i); break; - case CPUINFO_INT_INPUT_STATE + 0: tms99xx_set_irq_line(cpustate, 0, info->i); break; - case CPUINFO_INT_INPUT_STATE + 1: tms99xx_set_irq_line(cpustate, 1, info->i); break; - case CPUINFO_INT_INPUT_STATE + 2: tms99xx_set_irq_line(cpustate, 2, info->i); break; - -#if (TMS99XX_MODEL == TI990_10_ID) - case CPUINFO_INT_PC: - { - const unsigned top = (cpustate->cur_map == 0) ? 0xf800 : 0x10000; - - if ((cpustate->cur_map == 0) && (info->i >= 0x1ff800)) - /* intercept TPCS and CPU ROM */ - cpustate->PC = info->i - 0x1f0000; - else if (! cpustate->mapping_on) - cpustate->PC = (info->i < top) ? info->i : 0; - else - { - if ((info->i >= cpustate->map_files[cpustate->cur_map].bias[0]) - && (info->i <= (cpustate->map_files[cpustate->cur_map].bias[0]+cpustate->map_files[cpustate->cur_map].limit[0]))) - cpustate->PC = info->i - cpustate->map_files[cpustate->cur_map].bias[0]; - else if ((info->i > (cpustate->map_files[cpustate->cur_map].bias[1]+cpustate->map_files[cpustate->cur_map].limit[0])) - && (info->i <= (cpustate->map_files[cpustate->cur_map].bias[1]+cpustate->map_files[cpustate->cur_map].limit[1]))) - cpustate->PC = info->i - cpustate->map_files[cpustate->cur_map].bias[1]; - else if ((info->i > (cpustate->map_files[cpustate->cur_map].bias[2]+cpustate->map_files[cpustate->cur_map].limit[0])) - && (info->i > (cpustate->map_files[cpustate->cur_map].bias[2]+cpustate->map_files[cpustate->cur_map].limit[1])) - && (info->i <= (cpustate->map_files[cpustate->cur_map].bias[2]+cpustate->map_files[cpustate->cur_map].limit[2]))) - cpustate->PC = info->i - cpustate->map_files[cpustate->cur_map].bias[2]; - else - { - /*if ((info->i < top) - && (info->i > cpustate->map_files[cpustate->cur_map].limit[0]) - && (info->i > cpustate->map_files[cpustate->cur_map].limit[1]) - && (info->i > cpustate->map_files[cpustate->cur_map].limit[2])) - cpustate->PC = info->i; - else*/ - cpustate->PC = 0; - } - /*if (info->i >= top) - cpustate->PC = 0;*/ - } - cpustate->PC &= 0xfffe; - } - break; -#else - case CPUINFO_INT_PC: -#endif - case CPUINFO_INT_REGISTER + TMS9900_PC: cpustate->PC = info->i & 0xfffe; break; - case CPUINFO_INT_REGISTER + TMS9900_IR: cpustate->IR = info->i; break; - case CPUINFO_INT_SP: - case CPUINFO_INT_REGISTER + TMS9900_WP: cpustate->WP = info->i & 0xfffe; break; - case CPUINFO_INT_REGISTER + TMS9900_STATUS: cpustate->STATUS = info->i; getstat(cpustate); break; - case CPUINFO_INT_REGISTER + TMS9900_R0: WRITEREG_DEBUG(cpustate, R0, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R1: WRITEREG_DEBUG(cpustate, R1, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R2: WRITEREG_DEBUG(cpustate, R2, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R3: WRITEREG_DEBUG(cpustate, R3, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R4: WRITEREG_DEBUG(cpustate, R4, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R5: WRITEREG_DEBUG(cpustate, R5, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R6: WRITEREG_DEBUG(cpustate, R6, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R7: WRITEREG_DEBUG(cpustate, R7, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R8: WRITEREG_DEBUG(cpustate, R8, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R9: WRITEREG_DEBUG(cpustate, R9, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R10: WRITEREG_DEBUG(cpustate, R10, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R11: WRITEREG_DEBUG(cpustate, R11, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R12: WRITEREG_DEBUG(cpustate, R12, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R13: WRITEREG_DEBUG(cpustate, R13, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R14: WRITEREG_DEBUG(cpustate, R14, info->i); break; - case CPUINFO_INT_REGISTER + TMS9900_R15: WRITEREG_DEBUG(cpustate, R15, info->i); break; - } -} - - - -/************************************************************************** - * Generic get_info - **************************************************************************/ - -void TMS99XX_GET_INFO(legacy_cpu_device *device, UINT32 state, cpuinfo *info) -{ - tms99xx_state *cpustate = (device != NULL && device->token() != NULL) ? get_safe_token(device) : NULL; - switch (state) - { - /* --- the following bits of info are returned as 64-bit signed integers --- */ - case CPUINFO_INT_CONTEXT_SIZE: info->i = sizeof(tms99xx_state); break; - case CPUINFO_INT_INPUT_LINES: info->i = 3; break; - case CPUINFO_INT_DEFAULT_IRQ_VECTOR: info->i = 0; break; - case CPUINFO_INT_ENDIANNESS: info->i = ENDIANNESS_BIG; break; - case CPUINFO_INT_CLOCK_MULTIPLIER: info->i = 1; break; - case CPUINFO_INT_CLOCK_DIVIDER: info->i = 1; break; - case CPUINFO_INT_MIN_INSTRUCTION_BYTES: info->i = 2; break; - case CPUINFO_INT_MAX_INSTRUCTION_BYTES: info->i = 6;/*8 with 990/12, 99105, 99110*/break; - case CPUINFO_INT_MIN_CYCLES: info->i = 1; break; - case CPUINFO_INT_MAX_CYCLES: info->i = 10;/*TODO: compute this value*/break; - -#if (USE_16_BIT_ACCESSORS) - case CPUINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 16; break; -#else - case CPUINFO_INT_DATABUS_WIDTH + AS_PROGRAM: info->i = 8; break; -#endif - case CPUINFO_INT_ADDRBUS_WIDTH + AS_PROGRAM: -#if (TMS99XX_MODEL == TI990_10_ID) - /* this CPU has a mapper to expand the address space */ - info->i = 21; -#elif (TMS99XX_MODEL == TMS9900_ID) || (TMS99XX_MODEL == TMS9940_ID) || (TMS99XX_MODEL == TMS9985_ID) || (TMS99XX_MODEL == TMS9995_ID) - /* basic 16-bit address bus */ - info->i = 16; -#elif (TMS99XX_MODEL == TMS9980_ID) - /* 2 address MSBits are unconnected */ - info->i = 14; -#else - #warning "Please check how many address bits your CPU has." - info->i = 16; -#endif - break; - case CPUINFO_INT_ADDRBUS_SHIFT + AS_PROGRAM: info->i = 0; break; - case CPUINFO_INT_DATABUS_WIDTH + AS_DATA: info->i = 0; break; - case CPUINFO_INT_ADDRBUS_WIDTH + AS_DATA: info->i = 0; break; - case CPUINFO_INT_ADDRBUS_SHIFT + AS_DATA: info->i = 0; break; - case CPUINFO_INT_DATABUS_WIDTH + AS_IO: info->i = 8; break; - case CPUINFO_INT_ADDRBUS_WIDTH + AS_IO: -#if (TMS99XX_MODEL == TI990_10_ID) - /* 3 MSBs do exist, although they are not connected (don't ask...) */ - info->i = 15; -#elif (TMS99XX_MODEL == TMS9900_ID) - /* 3 MSBs are external instruction code */ - info->i = 12; -#elif (TMS99XX_MODEL == TMS9980_ID) - /* 2 bits unused, and 2 MSBs are external instruction code */ - info->i = 13; -#elif (TMS99XX_MODEL == TMS9940_ID) - /* 9 internal address lines (8 external) */ - info->i = 9; -#elif (TMS99XX_MODEL == TMS9995_ID) - /* 15-bit address and data bus lines D0-D2 provide the external instruction code */ - info->i = 15+3; -#else - #warning "I don't know how your processor handles CRU." - info->i = 15; -#endif - break; - case CPUINFO_INT_ADDRBUS_SHIFT + AS_IO: info->i = 0; break; - -/* not implemented */ -/* case CPUINFO_INT_INPUT_STATE + INPUT_LINE_NMI: info->i = get_irq_line(INPUT_LINE_NMI); break; - case CPUINFO_INT_INPUT_STATE + 0: info->i = get_irq_line(0); break; - case CPUINFO_INT_INPUT_STATE + 1: info->i = get_irq_line(1); break; - case CPUINFO_INT_INPUT_STATE + 2: info->i = get_irq_line(2); break;*/ - - case CPUINFO_INT_PREVIOUSPC: /* not implemented */ break; - -#if (TMS99XX_MODEL == TI990_10_ID) - case CPUINFO_INT_PC: - if ((cpustate->cur_map == 0) && (cpustate->PC >= 0xf800)) - /* intercept TPCS and CPU ROM */ - info->i = 0x1f0000+cpustate->PC; - else if (! cpustate->mapping_on) - info->i = cpustate->PC; - else - { - int map_index; - - if (cpustate->PC <= cpustate->map_files[cpustate->cur_map].limit[0]) - map_index = 0; - else if (cpustate->PC <= cpustate->map_files[cpustate->cur_map].limit[1]) - map_index = 1; - else if (cpustate->PC <= cpustate->map_files[cpustate->cur_map].limit[2]) - map_index = 2; - else - { - info->i = cpustate->PC; - break; - } - - info->i = cpustate->map_files[cpustate->cur_map].bias[map_index]+cpustate->PC; - } - break; -#else - case CPUINFO_INT_PC: -#endif - case CPUINFO_INT_REGISTER + TMS9900_PC: info->i = cpustate->PC; break; - case CPUINFO_INT_REGISTER + TMS9900_IR: info->i = cpustate->IR; break; - case CPUINFO_INT_SP: - case CPUINFO_INT_REGISTER + TMS9900_WP: info->i = cpustate->WP; break; - case CPUINFO_INT_REGISTER + TMS9900_STATUS: setstat(cpustate); info->i = cpustate->STATUS; break; - case CPUINFO_INT_REGISTER + TMS9900_R0: info->i = READREG_DEBUG(cpustate, R0); break; - case CPUINFO_INT_REGISTER + TMS9900_R1: info->i = READREG_DEBUG(cpustate, R1); break; - case CPUINFO_INT_REGISTER + TMS9900_R2: info->i = READREG_DEBUG(cpustate, R2); break; - case CPUINFO_INT_REGISTER + TMS9900_R3: info->i = READREG_DEBUG(cpustate, R3); break; - case CPUINFO_INT_REGISTER + TMS9900_R4: info->i = READREG_DEBUG(cpustate, R4); break; - case CPUINFO_INT_REGISTER + TMS9900_R5: info->i = READREG_DEBUG(cpustate, R5); break; - case CPUINFO_INT_REGISTER + TMS9900_R6: info->i = READREG_DEBUG(cpustate, R6); break; - case CPUINFO_INT_REGISTER + TMS9900_R7: info->i = READREG_DEBUG(cpustate, R7); break; - case CPUINFO_INT_REGISTER + TMS9900_R8: info->i = READREG_DEBUG(cpustate, R8); break; - case CPUINFO_INT_REGISTER + TMS9900_R9: info->i = READREG_DEBUG(cpustate, R9); break; - case CPUINFO_INT_REGISTER + TMS9900_R10: info->i = READREG_DEBUG(cpustate, R10); break; - case CPUINFO_INT_REGISTER + TMS9900_R11: info->i = READREG_DEBUG(cpustate, R11); break; - case CPUINFO_INT_REGISTER + TMS9900_R12: info->i = READREG_DEBUG(cpustate, R12); break; - case CPUINFO_INT_REGISTER + TMS9900_R13: info->i = READREG_DEBUG(cpustate, R13); break; - case CPUINFO_INT_REGISTER + TMS9900_R14: info->i = READREG_DEBUG(cpustate, R14); break; - case CPUINFO_INT_REGISTER + TMS9900_R15: info->i = READREG_DEBUG(cpustate, R15); break; - - /* --- the following bits of info are returned as pointers to data or functions --- */ - case CPUINFO_FCT_SET_INFO: info->setinfo = CPU_SET_INFO_NAME(tms99xx); break; - case CPUINFO_FCT_INIT: info->init = CPU_INIT_NAME(tms99xx); break; - case CPUINFO_FCT_RESET: info->reset = CPU_RESET_NAME(tms99xx); break; - case CPUINFO_FCT_EXIT: info->exit = CPU_EXIT_NAME(tms99xx); break; - case CPUINFO_FCT_EXECUTE: info->execute = CPU_EXECUTE_NAME(tms99xx); break; - case CPUINFO_FCT_BURN: info->burn = NULL; break; - case CPUINFO_FCT_DISASSEMBLE: info->disassemble = CPU_DISASSEMBLE_NAME(tms99xx); break; - case CPUINFO_PTR_INSTRUCTION_COUNTER: info->icount = &cpustate->icount; break; - - /* --- the following bits of info are returned as NULL-terminated strings --- */ - case CPUINFO_STR_NAME: strcpy(info->s, TMS99XX_device_get_name); break; - case CPUINFO_STR_SHORTNAME: strcpy(info->s, TMS99XX_device_get_shortname); break; - - case CPUINFO_STR_FAMILY: strcpy(info->s, "Texas Instruments 9900L"); break; - case CPUINFO_STR_VERSION: strcpy(info->s, "2.0"); break; - case CPUINFO_STR_SOURCE_FILE: strcpy(info->s, __FILE__); break; - case CPUINFO_STR_CREDITS: strcpy(info->s, "C TMS9900 emulator by Edward Swartz, initially converted for Mame by M.Coates, updated by R. Nabet"); break; - - case CPUINFO_STR_FLAGS: - sprintf(info->s, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", - cpustate->WP & 0x8000 ? 'L':'.', - cpustate->WP & 0x4000 ? 'A':'.', - cpustate->WP & 0x2000 ? 'E':'.', - cpustate->WP & 0x1000 ? 'C':'.', - cpustate->WP & 0x0800 ? 'V':'.', - cpustate->WP & 0x0400 ? 'P':'.', - cpustate->WP & 0x0200 ? 'X':'.', - cpustate->WP & 0x0100 ? '?':'.', - cpustate->WP & 0x0080 ? '?':'.', - cpustate->WP & 0x0040 ? '?':'.', - cpustate->WP & 0x0020 ? '?':'.', - cpustate->WP & 0x0010 ? '?':'.', - cpustate->WP & 0x0008 ? 'I':'.', - cpustate->WP & 0x0004 ? 'I':'.', - cpustate->WP & 0x0002 ? 'I':'.', - cpustate->WP & 0x0001 ? 'I':'.'); - break; - - case CPUINFO_STR_REGISTER + TMS9900_PC: sprintf(info->s, "PC :%04X", cpustate->PC); break; - case CPUINFO_STR_REGISTER + TMS9900_IR: sprintf(info->s, "IR :%04X", cpustate->IR); break; - case CPUINFO_STR_REGISTER + TMS9900_WP: sprintf(info->s, "WP :%04X", cpustate->WP); break; - case CPUINFO_STR_REGISTER + TMS9900_STATUS: sprintf(info->s, "ST :%04X", cpustate->STATUS); break; - - case CPUINFO_STR_REGISTER + TMS9900_R0: sprintf(info->s, "R0 :%04X", READREG_DEBUG(cpustate, R0)); break; - case CPUINFO_STR_REGISTER + TMS9900_R1: sprintf(info->s, "R1 :%04X", READREG_DEBUG(cpustate, R1)); break; - case CPUINFO_STR_REGISTER + TMS9900_R2: sprintf(info->s, "R2 :%04X", READREG_DEBUG(cpustate, R2)); break; - case CPUINFO_STR_REGISTER + TMS9900_R3: sprintf(info->s, "R3 :%04X", READREG_DEBUG(cpustate, R3)); break; - case CPUINFO_STR_REGISTER + TMS9900_R4: sprintf(info->s, "R4 :%04X", READREG_DEBUG(cpustate, R4)); break; - case CPUINFO_STR_REGISTER + TMS9900_R5: sprintf(info->s, "R5 :%04X", READREG_DEBUG(cpustate, R5)); break; - case CPUINFO_STR_REGISTER + TMS9900_R6: sprintf(info->s, "R6 :%04X", READREG_DEBUG(cpustate, R6)); break; - case CPUINFO_STR_REGISTER + TMS9900_R7: sprintf(info->s, "R7 :%04X", READREG_DEBUG(cpustate, R7)); break; - case CPUINFO_STR_REGISTER + TMS9900_R8: sprintf(info->s, "R8 :%04X", READREG_DEBUG(cpustate, R8)); break; - case CPUINFO_STR_REGISTER + TMS9900_R9: sprintf(info->s, "R9 :%04X", READREG_DEBUG(cpustate, R9)); break; - case CPUINFO_STR_REGISTER + TMS9900_R10: sprintf(info->s, "R10:%04X", READREG_DEBUG(cpustate, R10)); break; - case CPUINFO_STR_REGISTER + TMS9900_R11: sprintf(info->s, "R11:%04X", READREG_DEBUG(cpustate, R11)); break; - case CPUINFO_STR_REGISTER + TMS9900_R12: sprintf(info->s, "R12:%04X", READREG_DEBUG(cpustate, R12)); break; - case CPUINFO_STR_REGISTER + TMS9900_R13: sprintf(info->s, "R13:%04X", READREG_DEBUG(cpustate, R13)); break; - case CPUINFO_STR_REGISTER + TMS9900_R14: sprintf(info->s, "R14:%04X", READREG_DEBUG(cpustate, R14)); break; - case CPUINFO_STR_REGISTER + TMS9900_R15: sprintf(info->s, "R15:%04X", READREG_DEBUG(cpustate, R15)); break; - } -} diff --git a/src/emu/cpu/tms9900/ti990_10.c b/src/emu/cpu/tms9900/ti990_10.c deleted file mode 100644 index b6cd8d32d41..00000000000 --- a/src/emu/cpu/tms9900/ti990_10.c +++ /dev/null @@ -1,148 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf - -/* - Texas Instruments TI990/10 CPU board - - The first member of the family was actually the ti990/10 minicomputer, - released in 1975. tms9900 was released in 1976, and has the same - instruction set as ti990/10: however, tms9900 is slower, it does not - support privileges and memory mapping, and illegal instructions do not - cause an error interrupt. - - The ti990 family later evoluted into the huge ti990/12 system, with support - for 144 different instructions, and microcode programming in case some user - found it was not enough. ti990/10 was eventually replaced by a cheaper - ti990/10a board, built around a tms99000 microprocessor. - - tms99000 is the successor to both ti9900 and ti990/10. It supports - privileges, and has a coprocessor interface which enables the use of an - external memory mapper. Additionnally, it can use a Macrostore ROM to - emulate additional instructions. - - **** This is WORK IN PROGRESS **** -*/ - -#include "ti990_10.h" - -/* - The following defines can be set to 0 or 1 to disable or enable certain - output in the log. -*/ -// Emulation setup -#define TRACE_SETUP 0 - -// Emulation details -#define TRACE_EMU 0 - -/**************************************************************************** - Constructor for TI 990/10 - The CRU mask is related to the bits, not to their addresses which are - twice their number. Accordingly, the TMS9900 has a CRU bitmask 0x0fff. -****************************************************************************/ - -ti990_10_device::ti990_10_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : cpu_device(mconfig, TI990_10, "TI990/10 CPU", tag, owner, clock, "ti990_10_cpu", __FILE__), - m_program_config("program", ENDIANNESS_BIG, 16, 16), - m_io_config("cru", ENDIANNESS_BIG, 8, 12), - m_prgspace(NULL), - m_cru(NULL) -{ -} - -ti990_10_device::~ti990_10_device() -{ -} - -void ti990_10_device::device_start() -{ - m_prgspace = &space(AS_PROGRAM); - m_cru = &space(AS_IO); - - // set our instruction counter - m_icountptr = &m_icount; - - state_add(STATE_GENPC, "curpc", PC).formatstr("%4s").noshow(); - state_add(STATE_GENFLAGS, "status", m_state_any).callimport().callexport().formatstr("%16s").noshow(); -} - -void ti990_10_device::device_stop() -{ - if (TRACE_SETUP) logerror("ti990_10: Deleting lookup tables\n"); -} - -/* - TI990_10 hard reset - The device reset is just the emulator's trigger for the reset procedure - which is invoked via the main loop. -*/ -void ti990_10_device::device_reset() -{ - if (TRACE_EMU) logerror("ti990_10: Device reset by emulator\n"); -} - -const address_space_config *ti990_10_device::memory_space_config(address_spacenum spacenum) const -{ - switch (spacenum) - { - case AS_PROGRAM: - return &m_program_config; - - case AS_IO: - return &m_io_config; - - default: - return NULL; - } -} - -void ti990_10_device::execute_run() -{ - do - { - // TODO: Complete the implementation - m_icount--; - } while (m_icount>0); -} - -void ti990_10_device::execute_set_input(int irqline, int state) -{ -} - -// ========================================================================== - -UINT32 ti990_10_device::execute_min_cycles() const -{ - return 2; -} - -// TODO: Compute this value, just a wild guess for the average -UINT32 ti990_10_device::execute_max_cycles() const -{ - return 10; -} - -UINT32 ti990_10_device::execute_input_lines() const -{ - return 2; -} - -// device_disasm_interface overrides -UINT32 ti990_10_device::disasm_min_opcode_bytes() const -{ - return 2; -} - -UINT32 ti990_10_device::disasm_max_opcode_bytes() const -{ - return 6; -} - -// TODO: check 9900dasm -offs_t ti990_10_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) -{ - extern CPU_DISASSEMBLE( tms9900 ); - return CPU_DISASSEMBLE_NAME(tms9900)(this, buffer, pc, oprom, opram, options); -} - -const device_type TI990_10 = &device_creator<ti990_10_device>; diff --git a/src/emu/cpu/tms9900/ti990_10.h b/src/emu/cpu/tms9900/ti990_10.h deleted file mode 100644 index f1d4e20ce3a..00000000000 --- a/src/emu/cpu/tms9900/ti990_10.h +++ /dev/null @@ -1,61 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - TI 990 CPU board - See ti990_10.c for documentation -*/ - -#ifndef __TI990_10_H__ -#define __TI990_10_H__ - -#include "emu.h" -#include "debugger.h" -#include "tms99com.h" - -class ti990_10_device : public cpu_device -{ -public: - ti990_10_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - ~ti990_10_device(); - -protected: - // device-level overrides - void device_start(); - void device_stop(); - void device_reset(); - - // device_execute_interface overrides - UINT32 execute_min_cycles() const; - UINT32 execute_max_cycles() const; - UINT32 execute_input_lines() const; - void execute_set_input(int irqline, int state); - void execute_run(); - - // device_disasm_interface overrides - UINT32 disasm_min_opcode_bytes() const; - UINT32 disasm_max_opcode_bytes() const; - offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options); - - const address_space_config* memory_space_config(address_spacenum spacenum) const; - - const address_space_config m_program_config; - const address_space_config m_io_config; - address_space* m_prgspace; - address_space* m_cru; - - // Cycle counter - int m_icount; - - // Hardware registers - UINT16 WP; // Workspace pointer - UINT16 PC; // Program counter - UINT16 ST; // Status register - -private: - UINT16 m_state_any; -}; - -// device type definition -extern const device_type TI990_10; - -#endif /* __TI990_10_H__ */ diff --git a/src/emu/cpu/tms9900/tms9900.c b/src/emu/cpu/tms9900/tms9900.c deleted file mode 100644 index 0029d9cf392..00000000000 --- a/src/emu/cpu/tms9900/tms9900.c +++ /dev/null @@ -1,2736 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - Texas Instruments TMS9900 - - +--------------------+ - V_BB | 1 o 64| /HOLD - V_CC | 2 63| /MEMEN - WAIT | 3 62| READY - /LOAD | 4 61| /WE - HOLDA | 5 60| CRUCLK - /RESET | 6 59| V_CC - IAQ | 7 58| - - PHI1 | 8 57| - - PHI2 | 9 56| D15 -+ LSB - LSB +- A14 |10 55| D14 | - | A13 |11 54| D13 | - | A12 |12 53| D12 | - | A11 |13 52| D11 | - Address | A10 |14 +--------+ 51| D10 | Data - bus | A9 |15 | | 50| D9 | bus - 32K * | A8 |16 | | 49| D8 | 16 bit - 16bit | A7 |17 | | 48| D7 | - | A6 |18 | | 47| D6 | - | A5 |19 +--------+ 46| D5 | - | A4 |20 45| D4 | - | A3 |21 44| D3 | - | A2 |22 43| D2 | - | A1 |23 42| D1 | - MSB +- A0 |24 41| D0 -+ MSB - PHI4 |25 40| V_SS - V_SS |26 39| - - V_DD |27 38| - - PHI3 |28 37| - - DBIN |29 36| IC0 -+ MSB - CRUOUT |30 35| IC1 | Interrupt - CRUIN |31 34| IC2 | level - /INTREQ |32 33| IC3 -+ LSB - +--------------------+ - - WAIT out Processor in wait state - /LOAD in Non-maskable interrupt - HOLDA out Hold acknowledge - /RESET in Reset - IAQ out Instruction acquisition - PHI1-4 in Clock phase inputs - DBIN out Data bus in input mode - CRUOUT out Communication register unit data output - CRUIN in Communication register unit data input - /INTREQ in Interrupt request - CRUCLK out Communication register unit clock output - /WE out Data available for memory write - READY in Memory ready for access - /MEMEN out Address bus contains memory address - /HOLD in External device acquires address and data bus lines - - V_BB -5V supply - V_CC +5V supply (pins 2 and 59 connected in parallel) - V_DD +12V supply - V_SS 0V Ground reference (pins 26 and 40 connected in parallel) - - A0-A14 out Address bus (32768 words of 16 bit width) - D0-A15 i/o Data bus - IC0-IC3 in Interrupt level (0-15) - - Note that Texas Instruments' bit numberings define bit 0 as the - most significant bit (different to most other systems). Also, the - system uses big-endian memory organisation: Storing the word 0x1234 at - address 0x0000 means that the byte 0x12 is stored at 0x0000 and byte 0x34 - is stored at 0x0001. - - The processor also knows byte-oriented operations (like add byte (AB), - move byte (MOVB)). This makes it necessary for the CPU to read the word - from the target memory location first, change the respective byte, and - write it back. - - See the TI-99/4A driver for an application of the TMS9900 processor - within an 8-bit data bus board layout (using a data bus multiplexer). - - Subcycle handling - - In this implementation we try to emulate the internal operations as - precisely as possible, following the technical specifications. We need - not try to be clock-precise with every tick; it suffices to perform - the proper number of operations within a given time span. - - For each command the CPU executes a microprogram which requires some - amount of cycles to complete. During this time the external clock continues - to issue pulses which can be used to control wait state creation. As we - do not emulate external clocks this implementation offers an extra output - "clock_out" (which, however, is available for the TMS9995) which pulses - at a rate of 3 MHz. External devices (e.g. memory controllers) may count - the pulses and pull down the READY line (with set_ready) as needed. - - Another possibility for creating wait states is to pull down the line - for some time set by a timer. This is done, for example, by circuits like - GROMs or speech synthesis processors (TMS52xx). - - TODO: - - Fine-tune cycles - - State save - - HOLD state should be tested; I don't have test cases yet - - Michael Zapf, June 2012 -*/ - -#include "tms9900.h" - -/* tms9900 ST register bits. */ -enum -{ - ST_LH = 0x8000, // Logical higher (unsigned comparison) - ST_AGT = 0x4000, // Arithmetical greater than (signed comparison) - ST_EQ = 0x2000, // Equal - ST_C = 0x1000, // Carry - ST_OV = 0x0800, // Overflow (when using signed operations) - ST_OP = 0x0400, // Odd parity (used with byte operations) - ST_X = 0x0200, // XOP - ST_IM = 0x000f // Interrupt mask -}; - -/* - The following defines can be set to 0 or 1 to disable or enable certain - output in the log. -*/ -// Emulation setup -#define TRACE_SETUP 0 - -// Emulation details -#define TRACE_EMU 0 - -// Location and command -#define TRACE_EXEC 0 - -// Memory operation -#define TRACE_MEM 0 - -// Address bus operation -#define TRACE_ADDRESSBUS 0 - -// Cycle count -#define TRACE_CYCLES 0 - -// Clock ticks -#define TRACE_CLOCK 0 - -// Wait states -#define TRACE_WAIT 0 - -// Interrupts -#define TRACE_INT 0 - -// CRU operation -#define TRACE_CRU 0 - -// Status register -#define TRACE_STATUS 0 - -// ALU details -#define TRACE_ALU 0 - -// Microinstruction level -#define TRACE_MICRO 0 - -/**************************************************************************** - Common constructor for TMS9900 and TMS9980A - The CRU mask is related to the bits, not to their addresses which are - twice their number. Accordingly, the TMS9900 has a CRU bitmask 0x0fff. -****************************************************************************/ - -tms99xx_device::tms99xx_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, int databus_width, int prg_addr_bits, int cru_addr_bits, device_t *owner, UINT32 clock, const char *shortname, const char *source) - : cpu_device(mconfig, type, name, tag, owner, clock, shortname, source), - m_program_config("program", ENDIANNESS_BIG, databus_width, prg_addr_bits), - m_io_config("cru", ENDIANNESS_BIG, 8, cru_addr_bits), - m_prgspace(NULL), - m_cru(NULL), - m_prgaddr_mask((1<<prg_addr_bits)-1), - m_cruaddr_mask((1<<cru_addr_bits)-1), - m_clock_out_line(*this), - m_wait_line(*this), - m_holda_line(*this), - m_iaq_line(*this), - m_get_intlevel(*this), - m_dbin_line(*this), - m_external_operation(*this) -{ -} - -tms99xx_device::~tms99xx_device() -{ -} - -/**************************************************************************** - Constructor for TMS9900 -****************************************************************************/ - -tms9900_device::tms9900_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms99xx_device(mconfig, TMS9900, "TMS9900", tag, 16, 16, 12, owner, clock, "tms9900", __FILE__) -{ -} - -enum -{ - TMS9900_PC=0, TMS9900_WP, TMS9900_STATUS, TMS9900_IR, - TMS9900_R0, TMS9900_R1, TMS9900_R2, TMS9900_R3, - TMS9900_R4, TMS9900_R5, TMS9900_R6, TMS9900_R7, - TMS9900_R8, TMS9900_R9, TMS9900_R10, TMS9900_R11, - TMS9900_R12, TMS9900_R13, TMS9900_R14, TMS9900_R15 -}; - -void tms99xx_device::device_start() -{ - // TODO: Restore state save feature - resolve_lines(); - m_prgspace = &space(AS_PROGRAM); - m_cru = &space(AS_IO); - - // set our instruction counter - m_icountptr = &m_icount; - - m_state_any = 0; - PC = 0; - m_hold_state = false; - - // add the states for the debugger - for (int i=0; i < 20; i++) - { - // callimport = need to use the state_import method to write to the state variable - // callexport = need to use the state_export method to read the state variable - state_add(i, s_statename[i], m_state_any).callimport().callexport().formatstr("%04X"); - } - state_add(STATE_GENPC, "curpc", PC).formatstr("%4s").noshow(); - state_add(STATE_GENFLAGS, "status", m_state_any).callimport().callexport().formatstr("%16s").noshow(); - - build_command_lookup_table(); - - m_program = NULL; -} - -void tms99xx_device::device_stop() -{ - int k = 0; - if (TRACE_SETUP) logerror("tms99xx: Deleting lookup tables\n"); - while (m_lotables[k]!=NULL) delete[] m_lotables[k++]; -} - -/* - External connections -*/ -void tms99xx_device::resolve_lines() -{ - // Resolve our external connections - m_external_operation.resolve(); - m_get_intlevel.resolve(); - m_iaq_line.resolve(); - m_clock_out_line.resolve(); - m_wait_line.resolve(); - m_holda_line.resolve(); - m_dbin_line.resolve(); // we need this for the set_address operation -} - -/* - TMS9900 hard reset - The device reset is just the emulator's trigger for the reset procedure - which is invoked via the main loop. -*/ -void tms99xx_device::device_reset() -{ - if (TRACE_EMU) logerror("tms99xx: Device reset by emulator\n"); - m_reset = true; - m_check_ready = false; - m_wait_state = false; - ST = 0; - m_irq_state = false; -} - -const char* tms99xx_device::s_statename[20] = -{ - "PC", "WP", "ST", "IR", - "R0", "R1", "R2", "R3", - "R4", "R5", "R6", "R7", - "R8", "R9", "R10","R11", - "R12","R13","R14","R15" -}; - -/* - Write the contents of a register by external input (debugger) -*/ -void tms99xx_device::state_import(const device_state_entry &entry) -{ - int index = entry.index(); - switch (entry.index()) - { - case STATE_GENFLAGS: - // no action here; we do not allow import, as the flags are all - // bits of the STATUS register - break; - case TMS9900_PC: - PC = (UINT16)(m_state_any & m_prgaddr_mask & 0xfffe); - break; - case TMS9900_WP: - WP = (UINT16)(m_state_any & m_prgaddr_mask & 0xfffe); - break; - case TMS9900_STATUS: - ST = (UINT16)m_state_any; - break; - case TMS9900_IR: - IR = (UINT16)m_state_any; - break; - default: - // Workspace registers - if (index <= TMS9900_R15) - write_workspace_register_debug(index-TMS9900_R0, (UINT16)m_state_any); - break; - } -} - -/* - Reads the contents of a register for display in the debugger. -*/ -void tms99xx_device::state_export(const device_state_entry &entry) -{ - int index = entry.index(); - switch (entry.index()) - { - case STATE_GENFLAGS: - m_state_any = ST; - break; - case TMS9900_PC: - m_state_any = PC; - break; - case TMS9900_WP: - m_state_any = WP; - break; - case TMS9900_STATUS: - m_state_any = ST; - break; - case TMS9900_IR: - m_state_any = IR; - break; - default: - // Workspace registers - if (index <= TMS9900_R15) - m_state_any = read_workspace_register_debug(index-TMS9900_R0); - break; - } -} - -/* - state_string_export - export state as a string for the debugger -*/ -void tms99xx_device::state_string_export(const device_state_entry &entry, std::string &str) -{ - static const char *statestr = "LAECOPX-----IIII"; - char flags[17]; - memset(flags, 0x00, ARRAY_LENGTH(flags)); - UINT16 val = 0x8000; - if (entry.index()==STATE_GENFLAGS) - { - for (int i=0; i < 16; i++) - { - flags[i] = ((val & ST)!=0)? statestr[i] : '.'; - val = (val >> 1) & 0x7fff; - } - } - str.assign(flags); -} - -/**************************************************************************/ - -UINT16 tms99xx_device::read_workspace_register_debug(int reg) -{ - int temp = m_icount; - m_prgspace->set_debugger_access(true); - UINT16 value = m_prgspace->read_word((WP+(reg<<1)) & m_prgaddr_mask & 0xfffe); - m_prgspace->set_debugger_access(false); - m_icount = temp; - return value; -} - -void tms99xx_device::write_workspace_register_debug(int reg, UINT16 data) -{ - int temp = m_icount; - m_prgspace->set_debugger_access(true); - m_prgspace->write_word((WP+(reg<<1)) & m_prgaddr_mask & 0xfffe, data); - m_prgspace->set_debugger_access(false); - m_icount = temp; -} - -const address_space_config *tms99xx_device::memory_space_config(address_spacenum spacenum) const -{ - switch (spacenum) - { - case AS_PROGRAM: - return &m_program_config; - - case AS_IO: - return &m_io_config; - - default: - return NULL; - } -} - -/************************************************************************** - Microprograms for the CPU instructions - - The actions which are specific to the respective instruction are - invoked by repeated calls of ALU_xxx; each call increases a state - variable so that on the next call, the next part can be processed. - This saves us a lot of additional functions. -**************************************************************************/ - -/* - Define the indices for the micro-operation table. This is done for the sake - of a simpler microprogram definition as an UINT8[]. -*/ -enum -{ - IAQ = 0, - MEMORY_READ, - MEMORY_WRITE, - REG_READ, - REG_WRITE, - CRU_INPUT, - CRU_OUTPUT, - DATA_DERIVE, - RET, - ABORT, - END, - - ALU_NOP, - ALU_CLR, - ALU_SETADDR, - ALU_ADDONE, - ALU_SETADDR_ADDONE, - ALU_PCADDR_ADVANCE, - ALU_SOURCE, - ALU_ADDREG, - ALU_IMM, - ALU_REG, - ALU_F1, - ALU_COMP, - ALU_F3, - ALU_MPY, - ALU_DIV, - ALU_XOP, - ALU_CLR_SWPB, - ALU_ABS, - ALU_X, - ALU_B, - ALU_BLWP, - ALU_LDCR, - ALU_STCR, - ALU_SBZ_SBO, - ALU_TB, - ALU_JMP, - ALU_SHIFT, - ALU_AI_ORI, - ALU_CI, - ALU_LI, - ALU_LWPI, - ALU_LIMI, - ALU_STWP_STST, - ALU_EXT, - ALU_RTWP, - ALU_INT -}; - - -#define MICROPROGRAM(_MP) \ - static const UINT8 _MP[] = - -/* - This is a kind of subroutine with 6 variants. Might be done in countless - better ways, but will suffice for now. Each variant has at most 8 steps - RET will return to the caller. - The padding simplifies the calculation of the start address: We just - take the Ts field as an index. In the last two cases we add an offset of 8 - if we have an indexed (resp. a byte) operation. -*/ -MICROPROGRAM(data_derivation) -{ - REG_READ, RET, 0, 0, 0, 0, 0, 0, // Rx (00) - 0, 0, 0, 0, 0, 0, 0, 0, - REG_READ, ALU_SETADDR, MEMORY_READ, RET, 0, 0, 0, 0, // *Rx (01) - 0, 0, 0, 0, 0, 0, 0, 0, - ALU_CLR, ALU_PCADDR_ADVANCE, MEMORY_READ, ALU_ADDREG, MEMORY_READ, RET, 0, 0, // @sym (10) - REG_READ, ALU_PCADDR_ADVANCE, MEMORY_READ, ALU_ADDREG, MEMORY_READ, RET, 0, 0, // @sym(Rx) (10) - REG_READ, ALU_SETADDR_ADDONE, ALU_ADDONE, REG_WRITE, MEMORY_READ, RET, 0, 0, // *Rx+ (word) (11) - REG_READ, ALU_SETADDR_ADDONE, REG_WRITE, MEMORY_READ, RET, 0, 0, 0 // *Rx+ (byte) (11) -}; - -MICROPROGRAM(f1_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_SOURCE, // Store the word - DATA_DERIVE, - ALU_F1, - MEMORY_WRITE, - END -}; - -MICROPROGRAM(comp_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_SOURCE, - DATA_DERIVE, - ALU_COMP, - ALU_NOP, // Compare operations do not write back any data - END -}; - -MICROPROGRAM(f3_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_F3, - MEMORY_READ, // We have to distinguish this from the C/CB microprogram above - ALU_F3, - ALU_NOP, // Compare operations do not write back any data - END -}; - -MICROPROGRAM(xor_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_F3, - MEMORY_READ, - ALU_F3, - MEMORY_WRITE, // XOR again must write back data, cannot reuse f3_mp - END -}; - -MICROPROGRAM(mult_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_MPY, // Save the value; put register number in m_regnumber - MEMORY_READ, - ALU_MPY, // 18 cycles for multiplication - MEMORY_WRITE, // Write the high word - ALU_MPY, // Get low word, increase m_address - MEMORY_WRITE, - END -}; - -MICROPROGRAM(div_mp) -{ - ALU_NOP, - DATA_DERIVE, // Get divisor - ALU_DIV, // 0 Store divisor and get register number - MEMORY_READ, // Read register - ALU_DIV, // 1 Check overflow, increase address (or abort here) - ABORT, - MEMORY_READ, // Read subsequent word (if reg=15 this is behind the workspace) - ALU_DIV, // 2 Calculate quotient (takes variable amount of cycles; at least 32 machine cycles), set register number - MEMORY_WRITE, // Write quotient into register - ALU_DIV, // 3 Get remainder - MEMORY_WRITE, // Write remainder - END -}; - -MICROPROGRAM(xop_mp) -{ - ALU_NOP, - DATA_DERIVE, // Get argument - ALU_XOP, // 0 Save the address of the source operand, set address = 0x0040 + xopNr*4, 6 cycles - MEMORY_READ, // Read the new WP - ALU_XOP, // 1 Save old WP, set new WP, get the source operand address - MEMORY_WRITE, // Write the address of the source operand into the new R11 - ALU_XOP, // 2 - MEMORY_WRITE, // Write the ST into the new R15 - ALU_XOP, // 3 - MEMORY_WRITE, // Write the PC into the new R14 - ALU_XOP, // 4 - MEMORY_WRITE, // Write the WP into the new R13 - ALU_XOP, // 5 Set the X bit in the ST - MEMORY_READ, // Read the new PC - ALU_XOP, // 6 Set the new PC - END -}; - -MICROPROGRAM(clr_swpb_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_CLR_SWPB, - MEMORY_WRITE, - END -}; - -MICROPROGRAM(abs_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_ABS, // two cycles - MEMORY_WRITE, // skipped when ABS is not performed - ALU_NOP, - END -}; - -MICROPROGRAM(x_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_X, - END -}; - -MICROPROGRAM(b_mp) // Branch -{ - ALU_NOP, - DATA_DERIVE, - ALU_B, - END -}; - -MICROPROGRAM(bl_mp) // Branch and Link -{ - ALU_NOP, - DATA_DERIVE, - ALU_B, - ALU_NOP, - MEMORY_WRITE, - END -}; - -MICROPROGRAM(blwp_mp) // Branch and Load WP -{ - ALU_NOP, - DATA_DERIVE, // Get argument - ALU_BLWP, // 0 Save old WP, set new WP, save position - ALU_NOP, - MEMORY_WRITE, // write ST to R15 - ALU_BLWP, // 1 - MEMORY_WRITE, // write PC to R14 - ALU_BLWP, // 2 - MEMORY_WRITE, // write WP to R13 - ALU_BLWP, // 3 Get saved position - MEMORY_READ, // Read new PC - ALU_BLWP, // 4 Set new PC - END -}; - -MICROPROGRAM(ldcr_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_SOURCE, - ALU_NOP, - ALU_LDCR, - ALU_NOP, - MEMORY_READ, - ALU_LDCR, - CRU_OUTPUT, - ALU_NOP, - END -}; - -MICROPROGRAM(stcr_mp) -{ - ALU_NOP, - DATA_DERIVE, - ALU_SOURCE, // Store address and value - ALU_STCR, // 0 Set register_number = 12; 0 cycles (already done before) - MEMORY_READ, - ALU_STCR, // 1 Prepare CRU access - ALU_NOP, - CRU_INPUT, - ALU_STCR, // 2 Create result; Cycles = 5 + (8-#C-1) or + (16-#C) - ALU_NOP, - ALU_NOP, - ALU_NOP, - MEMORY_WRITE, - END -}; - -MICROPROGRAM(sbz_sbo_mp) -{ - ALU_SBZ_SBO, - ALU_NOP, - MEMORY_READ, - ALU_SBZ_SBO, - CRU_OUTPUT, - END -}; - -MICROPROGRAM(tb_mp) -{ - ALU_TB, - MEMORY_READ, - ALU_TB, - CRU_INPUT, - ALU_TB, - END -}; - -MICROPROGRAM(jmp_mp) -{ - ALU_NOP, - ALU_JMP, - ALU_JMP, - ALU_NOP, - END -}; - -MICROPROGRAM(shift_mp) -{ - ALU_SHIFT, - MEMORY_READ, - ALU_SHIFT, // 2 cycles if count != 0, else 4 - MEMORY_READ, // skipped if count != 0 - ALU_SHIFT, // skipped if count != 0 (4 cycles) - ALU_SHIFT, - MEMORY_WRITE, - ALU_NOP, - END -}; - -MICROPROGRAM(ai_ori_mp) -{ - ALU_REG, - MEMORY_READ, - ALU_IMM, - MEMORY_READ, - ALU_AI_ORI, - MEMORY_WRITE, - END -}; - -MICROPROGRAM(ci_mp) -{ - ALU_REG, - MEMORY_READ, - ALU_IMM, - MEMORY_READ, - ALU_CI, - ALU_NOP, - END -}; - -MICROPROGRAM(li_mp) -{ - ALU_IMM, - MEMORY_READ, - ALU_LI, // sets status bits - ALU_REG, // set register number - MEMORY_WRITE, - END -}; - -MICROPROGRAM(lwpi_mp) -{ - ALU_IMM, - MEMORY_READ, - ALU_NOP, - ALU_LWPI, // sets WP - END -}; - -MICROPROGRAM(limi_mp) -{ - ALU_IMM, - MEMORY_READ, - ALU_NOP, - ALU_LIMI, // sets interrupt mask in ST - ALU_NOP, - ALU_NOP, - END -}; - -MICROPROGRAM(stwp_stst_mp) -{ - ALU_STWP_STST, - ALU_REG, - MEMORY_WRITE, - END -}; - -MICROPROGRAM(external_mp) -{ - ALU_NOP, - ALU_NOP, - ALU_EXT, - ALU_NOP, - ALU_NOP, - END -}; - -MICROPROGRAM(rtwp_mp) -{ - ALU_NOP, - ALU_RTWP, - MEMORY_READ, - ALU_RTWP, // no cycles - MEMORY_READ, - ALU_RTWP, // no cycles - MEMORY_READ, - ALU_RTWP, - END -}; - -MICROPROGRAM(int_mp) -{ - ALU_NOP, - ALU_INT, // 0 Set address = 0 - MEMORY_READ, - ALU_INT, // 1 Save old WP, set new WP, save position - MEMORY_WRITE, // write ST to R15 - ALU_INT, // 2 - MEMORY_WRITE, // write PC to R14 - ALU_INT, // 3 - MEMORY_WRITE, // write WP to R13 - ALU_INT, // 4 Get saved position - MEMORY_READ, // Read new PC - ALU_INT, // 5 Set new PC - END -}; - -const tms99xx_device::ophandler tms99xx_device::s_microoperation[] = -{ - &tms99xx_device::acquire_instruction, - &tms99xx_device::mem_read, - &tms99xx_device::mem_write, - &tms99xx_device::register_read, - &tms99xx_device::register_write, - &tms99xx_device::cru_input_operation, - &tms99xx_device::cru_output_operation, - &tms99xx_device::data_derivation_subprogram, - &tms99xx_device::return_from_subprogram, - &tms99xx_device::abort_operation, - &tms99xx_device::command_completed, - - &tms99xx_device::alu_nop, - &tms99xx_device::alu_clear, - &tms99xx_device::alu_setaddr, - &tms99xx_device::alu_addone, - &tms99xx_device::alu_setaddr_addone, - &tms99xx_device::alu_pcaddr_advance, - &tms99xx_device::alu_source, - &tms99xx_device::alu_add_register, - &tms99xx_device::alu_imm, - &tms99xx_device::alu_reg, - - &tms99xx_device::alu_f1, - &tms99xx_device::alu_comp, - &tms99xx_device::alu_f3, - &tms99xx_device::alu_multiply, - &tms99xx_device::alu_divide, - &tms99xx_device::alu_xop, - &tms99xx_device::alu_clr_swpb, - &tms99xx_device::alu_abs, - &tms99xx_device::alu_x, - &tms99xx_device::alu_b, - &tms99xx_device::alu_blwp, - &tms99xx_device::alu_ldcr, - &tms99xx_device::alu_stcr, - &tms99xx_device::alu_sbz_sbo, - &tms99xx_device::alu_tb, - &tms99xx_device::alu_jmp, - &tms99xx_device::alu_shift, - &tms99xx_device::alu_ai_ori, - &tms99xx_device::alu_ci, - &tms99xx_device::alu_li, - &tms99xx_device::alu_lwpi, - &tms99xx_device::alu_limi, - &tms99xx_device::alu_stwp_stst, - &tms99xx_device::alu_external, - &tms99xx_device::alu_rtwp, - &tms99xx_device::alu_int -}; - -/***************************************************************************** - CPU instructions -*****************************************************************************/ - -/* - Available instructions -*/ -enum -{ - ILL=0, A, AB, ABS, AI, ANDI, B, BL, BLWP, C, - CB, CI, CKOF, CKON, CLR, COC, CZC, DEC, DECT, DIV, - IDLE, INC, INCT, INV, JEQ, JGT, JH, JHE, JL, JLE, - JLT, JMP, JNC, JNE, JNO, JOC, JOP, LDCR, LI, LIMI, - LREX, LWPI, MOV, MOVB, MPY, NEG, ORI, RSET, RTWP, S, - SB, SBO, SBZ, SETO, SLA, SOC, SOCB, SRA, SRC, SRL, - STCR, STST, STWP, SWPB, SZC, SZCB, TB, X, XOP, XOR, - INTR -}; - -/* - Formats: - - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - ----+------------------------------------------------+ - 1 | Opcode | B | Td | RegNr | Ts | RegNr | - +--------+---+----+------------+----+------------+ - 2 | Opcode | Displacement | - +-----------------------+------------------------+ - 3 | Opcode | RegNr | Ts | RegNr | - +-----------------+------------+----+------------+ - 4 | Opcode | Count | Ts | RegNr | - +-----------------+------------+----+------------+ - 5 | Opcode | Count | RegNr | - +-----------------------+-----------+------------+ - 6 | Opcode | Ts | RegNr | - +------------------------------+----+------------+ - 7 | Opcode |0| 0| 0| 0| 0 | - +---------------------------------+-+--+--+--+---+ - 8 | Opcode |0| RegNr | - +---------------------------------+-+------------+ - 9 | Opcode | Reg/Nr | Ts | RegNr | - +-----------------+------------+----+------------+ -*/ - -/* - Defines the number of bits from the left which are significant for the - command in the respective format. -*/ -static const int format_mask_len[] = -{ - 0, 4, 8, 6, 6, 8, 10, 16, 12, 6 -}; - -const tms99xx_device::tms_instruction tms99xx_device::s_command[] = -{ - // Opcode, ID, format, microprg - { 0x0200, LI, 8, li_mp }, - { 0x0220, AI, 8, ai_ori_mp }, - { 0x0240, ANDI, 8, ai_ori_mp }, - { 0x0260, ORI, 8, ai_ori_mp }, - { 0x0280, CI, 8, ci_mp }, - { 0x02a0, STWP, 8, stwp_stst_mp }, - { 0x02c0, STST, 8, stwp_stst_mp }, - { 0x02e0, LWPI, 8, lwpi_mp }, - { 0x0300, LIMI, 8, limi_mp }, - { 0x0340, IDLE, 7, external_mp }, - { 0x0360, RSET, 7, external_mp }, - { 0x0380, RTWP, 7, rtwp_mp }, - { 0x03a0, CKON, 7, external_mp }, - { 0x03c0, CKOF, 7, external_mp }, - { 0x03e0, LREX, 7, external_mp }, - { 0x0400, BLWP, 6, blwp_mp }, - { 0x0440, B, 6, b_mp }, - { 0x0480, X, 6, x_mp }, - { 0x04c0, CLR, 6, clr_swpb_mp }, - { 0x0500, NEG, 6, clr_swpb_mp }, - { 0x0540, INV, 6, clr_swpb_mp }, - { 0x0580, INC, 6, clr_swpb_mp }, - { 0x05c0, INCT, 6, clr_swpb_mp }, - { 0x0600, DEC, 6, clr_swpb_mp }, - { 0x0640, DECT, 6, clr_swpb_mp }, - { 0x0680, BL, 6, bl_mp }, - { 0x06c0, SWPB, 6, clr_swpb_mp }, - { 0x0700, SETO, 6, clr_swpb_mp }, - { 0x0740, ABS, 6, abs_mp }, - { 0x0800, SRA, 5, shift_mp }, - { 0x0900, SRL, 5, shift_mp }, - { 0x0a00, SLA, 5, shift_mp }, - { 0x0b00, SRC, 5, shift_mp }, - { 0x1000, JMP, 2, jmp_mp }, - { 0x1100, JLT, 2, jmp_mp }, - { 0x1200, JLE, 2, jmp_mp }, - { 0x1300, JEQ, 2, jmp_mp }, - { 0x1400, JHE, 2, jmp_mp }, - { 0x1500, JGT, 2, jmp_mp }, - { 0x1600, JNE, 2, jmp_mp }, - { 0x1700, JNC, 2, jmp_mp }, - { 0x1800, JOC, 2, jmp_mp }, - { 0x1900, JNO, 2, jmp_mp }, - { 0x1a00, JL, 2, jmp_mp }, - { 0x1b00, JH, 2, jmp_mp }, - { 0x1c00, JOP, 2, jmp_mp }, - { 0x1d00, SBO, 2, sbz_sbo_mp }, - { 0x1e00, SBZ, 2, sbz_sbo_mp }, - { 0x1f00, TB, 2, tb_mp }, - { 0x2000, COC, 3, f3_mp }, - { 0x2400, CZC, 3, f3_mp }, - { 0x2800, XOR, 3, xor_mp }, - { 0x2c00, XOP, 3, xop_mp }, - { 0x3000, LDCR, 4, ldcr_mp }, - { 0x3400, STCR, 4, stcr_mp }, - { 0x3800, MPY, 9, mult_mp }, - { 0x3c00, DIV, 9, div_mp }, - { 0x4000, SZC, 1, f1_mp }, - { 0x5000, SZCB, 1, f1_mp }, - { 0x6000, S, 1, f1_mp }, - { 0x7000, SB, 1, f1_mp }, - { 0x8000, C, 1, comp_mp }, - { 0x9000, CB, 1, comp_mp }, - { 0xa000, A, 1, f1_mp }, - { 0xb000, AB, 1, f1_mp }, - { 0xc000, MOV, 1, f1_mp }, - { 0xd000, MOVB, 1, f1_mp }, - { 0xe000, SOC, 1, f1_mp }, - { 0xf000, SOCB, 1, f1_mp } -}; - -/* - Create a B-tree for looking up the commands. Each node can carry up to - 16 entries, indexed by 4 consecutive bits in the opcode. - - Works as follows: - - Opcode = 0201 (Load immediate value into register 1) - Opcode = 0284 (Compare immediate value with register 4) - - Table: [ Table0, table1, table2, ... tableF ] - | - +-------+ - v - table0: [ table00, table01, table02, ... table0f ] - | - +-------------------------+ - v - table02: [ table020, table021, ... table028, ... table02f ] - | | | - v v v - Entry NULL Entry - for LI for CI - - For each level in the tree, four more bits are compared. The search - terminates when the number of compared bits is equal or higher than - the number of significant bits of the format of this opcode. The entry - points to the respective line in s_command. - - This way we can decode all format 1 commands by a single pass (including the - most frequent command MOV), and almost all commands by less than four passes. - - The disadvantage is that we have to build these tables from the opcode - list at runtime, and many positions are empty. But we do not need more - than 20 tables for the TMS command set. -*/ -void tms99xx_device::build_command_lookup_table() -{ - int i = 0; - int cmdindex = 0; - int bitcount; - const tms_instruction *inst; - UINT16 opcode; - int k = 0; - - m_command_lookup_table = new lookup_entry[16]; - // We use lotables as a list of allocated tables - to be able to delete them - // at the end. - m_lotables[k++] = m_command_lookup_table; - - lookup_entry* table = m_command_lookup_table; - for (int j=0; j < 16; j++) - { - table[j].entry = NULL; - table[j].next_digit = NULL; - } - - do - { - inst = &s_command[i]; - table = m_command_lookup_table; - if (TRACE_SETUP) logerror("tms99xx: === opcode=%04x, len=%d\n", inst->opcode, format_mask_len[inst->format]); - bitcount = 4; - opcode = inst->opcode; - cmdindex = (opcode>>12) & 0x000f; - - while (bitcount < format_mask_len[inst->format]) - { - // Descend - if (table[cmdindex].next_digit == NULL) - { - if (TRACE_SETUP) logerror("tms99xx: create new table at bitcount=%d for index=%d\n", bitcount, cmdindex); - table[cmdindex].next_digit = new lookup_entry[16]; - m_lotables[k++] = table[cmdindex].next_digit; - for (int j=0; j < 16; j++) - { - table[cmdindex].next_digit[j].next_digit = NULL; - table[cmdindex].next_digit[j].entry = NULL; - } - } - else - { - if (TRACE_SETUP) logerror("tms99xx: found a table at bitcount=%d\n", bitcount); - } - - table = table[cmdindex].next_digit; - - bitcount = bitcount+4; - opcode <<= 4; - cmdindex = (opcode>>12) & 0x000f; - if (TRACE_SETUP) logerror("tms99xx: next index=%x\n", cmdindex); - } - - if (TRACE_SETUP) logerror("tms99xx: bitcount=%d\n", bitcount); - // We are at the target level - // Need to fill in the same entry for all values in the bitcount - // (if a command needs 10 bits we have to copy it four - // times for all combinations with 12 bits) - for (int j=0; j < (1<<(bitcount-format_mask_len[inst->format])); j++) - { - if (TRACE_SETUP) logerror("tms99xx: opcode=%04x at position %d\n", inst->opcode, cmdindex+j); - table[cmdindex+j].entry = inst; - } - - i++; - } while (inst->opcode != 0xf000); - - m_lotables[k++] = NULL; - if (TRACE_SETUP) logerror("tms99xx: Allocated %d tables\n", k); -} - -/* - Main execution loop - - For each invocation of execute_run, a number of loop iterations has been - calculated before (m_icount). Each loop iteration is one clock cycle. - The loop must be executed for the number of times that corresponds to the - time until the next timer event. - - In this implementation, each loop iteration also causes the clock line to - pulse once. External devices may use this pulse to decrement counters - which control the READY line. - - Machine cycles to clock input: - - +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ - | | | | | | | | | | | | | | | | | | clock (1 of 4 phases) - +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ +-+ + - |-------|-------|-------|-------|---- cycles (2 clock pulses each) - - Wait states only have effect for memory operations. They are processed as - follows: - - 1) The CPU sets the address bus for reading. If READY is low, the CPU - waits for the next clock tick repeatedly until READY is high again. - When this is the case, the data bus is sampled on the next clock tick - and the read operation is complete. - - As we do not have a split-phase read operation in this emulation - we actually read the data bus instantly but wait for the READY line to - be high again. - - 2) The CPU sets the address bus for writing. In the same moment, the data - bus is loaded with the word to be written. On the next clock tick, - the CPU checks the READY line and waits until it is high. When READY - is high at a clock tick, the operation is complete on the next clock tick. -*/ -void tms99xx_device::execute_run() -{ - if (m_reset) service_interrupt(); - - if (TRACE_EMU) logerror("tms99xx: calling execute_run for %d cycles\n", m_icount); - do - { - // Only when last instruction has completed - if (m_program == NULL) - { - if (m_load_state) - { - logerror("tms99xx: LOAD interrupt\n"); - m_irq_level = LOAD_INT; - m_irq_state = false; - service_interrupt(); - } - else - { - // Interrupts are serviced when - // - an interrupt condition is signaled over INTREQ and - // - the level indicated by IC0-IC3 is lower than the interrupt mask value and - // - the previous instruction is not an XOP or BLWP - if (m_irq_state && (m_irq_level <= (ST & 0x000f)) && (m_command != XOP && m_command != BLWP)) - service_interrupt(); - } - } - - if (m_program == NULL && m_idle_state) - { - if (TRACE_WAIT) logerror("tms99xx: idle state\n"); - pulse_clock(1); - if (!m_external_operation.isnull()) - { - m_external_operation(IDLE_OP, 0, 0xff); - m_external_operation(IDLE_OP, 1, 0xff); - } - } - else - { - // Handle HOLD - // A HOLD request is signalled through the input line HOLD. - // The hold state will be entered with the next non-memory access cycle. - if (m_hold_state && - (m_program==NULL || - (m_program[MPC] != IAQ && - m_program[MPC] != MEMORY_READ && m_program[MPC] != MEMORY_WRITE && - m_program[MPC] != REG_READ && m_program[MPC] != REG_WRITE))) - { - if (TRACE_WAIT) logerror("tms99xx: hold\n"); - if (!m_hold_acknowledged) acknowledge_hold(); - pulse_clock(1); - } - else - { - // Normal operation - if (m_check_ready && m_ready == false) - { - // We are in a wait state - set_wait_state(true); - if (TRACE_WAIT) logerror("tms99xx: wait\n"); - // The clock output should be used to change the state of an outer - // device which operates the READY line - pulse_clock(1); - } - else - { - set_wait_state(false); - m_check_ready = false; - - if (m_program==NULL) m_op = IAQ; - else - { - m_op = m_program[MPC]; - } - if (TRACE_MICRO) logerror("tms99xx: MPC = %d, m_op = %d\n", MPC, m_op); - // Call the operation of the microprogram - (this->*s_microoperation[m_op])(); - // If we have multiple passes (as in the TMS9980) - m_pass--; - if (m_pass<=0) - { - m_pass = 1; - MPC++; - m_mem_phase = 1; - if (!m_iaq_line.isnull()) m_iaq_line(CLEAR_LINE); - } - } - } - } - } while (m_icount>0 && !m_reset); - if (TRACE_EMU) logerror("tms99xx: cycles expired; will return soon.\n"); -} - -/**************************************************************************/ - -/* - Interrupt input -*/ -void tms99xx_device::execute_set_input(int irqline, int state) -{ - if (irqline==INT_9900_RESET && state==ASSERT_LINE) - { - m_reset = true; - } - else - { - if (irqline == INT_9900_LOAD) - { - m_load_state = (state==ASSERT_LINE); - m_irq_level = -1; - m_reset = false; - } - else - { - m_irq_state = (state==ASSERT_LINE); - if (state==ASSERT_LINE) - { - m_irq_level = get_intlevel(state); - if (TRACE_INT) logerror("tms99xx: /INT asserted, level=%d, ST=%04x\n", m_irq_level, ST); - } - else - { - if (TRACE_INT) logerror("tms99xx: /INT cleared\n"); - } - } - } -} - -/* - This can be overloaded by variants of TMS99xx. -*/ -int tms99xx_device::get_intlevel(int state) -{ - if (!m_get_intlevel.isnull()) return m_get_intlevel(0); - return 0; -} - -void tms99xx_device::service_interrupt() -{ - m_program = int_mp; - m_command = INTR; - m_idle_state = false; - if (!m_external_operation.isnull()) m_external_operation(IDLE_OP, 0, 0xff); - - m_state = 0; - - if (!m_dbin_line.isnull()) m_dbin_line(ASSERT_LINE); - - // If reset, we just start with execution, otherwise we put the MPC - // on the first microinstruction, which also means that the main loop shall - // leave it where it is. So we pretend we have another pass to do. - m_pass = m_reset? 1 : 2; - - if (m_reset) - { - m_irq_level = RESET_INT; - - m_ready_bufd = true; - m_ready = true; - m_load_state = false; - m_hold_state = false; - m_hold_acknowledged = false; - m_wait_state = false; - IR = 0; - ST = 0; - m_mem_phase = 1; - - m_reset = false; - } - if (TRACE_INT) - { - switch (m_irq_level) - { - case RESET_INT: logerror("tms99xx: **** triggered a RESET interrupt\n"); break; - case LOAD_INT: logerror("tms99xx: **** triggered a LOAD (NMI) interrupt\n"); break; - default: logerror("tms99xx: ** triggered an interrupt on level %d\n", m_irq_level); break; - } - } - - MPC = 0; - m_first_cycle = m_icount; -} - -/* - Issue a pulse on the clock line. -*/ -void tms99xx_device::pulse_clock(int count) -{ - for (int i=0; i < count; i++) - { - if (!m_clock_out_line.isnull()) m_clock_out_line(ASSERT_LINE); - m_ready = m_ready_bufd; // get the latched READY state - if (!m_clock_out_line.isnull()) m_clock_out_line(CLEAR_LINE); - m_icount--; // This is the only location where we count down the cycles. - if (TRACE_CLOCK) - { - if (m_check_ready) logerror("tms99xx: pulse_clock, READY=%d\n", m_ready? 1:0); - else logerror("tms99xx: pulse_clock\n"); - } - } -} - -/* - Enter the hold state. -*/ -void tms99xx_device::set_hold(int state) -{ - m_hold_state = (state==ASSERT_LINE); - if (!m_hold_state) - { - m_hold_acknowledged = false; - if (!m_holda_line.isnull()) m_holda_line(CLEAR_LINE); - } -} - -/* - Acknowledge the HOLD request. -*/ -inline void tms99xx_device::acknowledge_hold() -{ - m_hold_acknowledged = true; - if (!m_holda_line.isnull()) m_holda_line(ASSERT_LINE); -} - -/* - Signal READY to the CPU. When cleared, the CPU enters wait states. This - becomes effective on a clock pulse. -*/ -void tms99xx_device::set_ready(int state) -{ - m_ready_bufd = (state==ASSERT_LINE); -} - -void tms99xx_device::abort_operation() -{ - command_completed(); -} - -/* - Enter or leave the wait state. We only operate the WAIT line when there is a change. -*/ -inline void tms99xx_device::set_wait_state(bool state) -{ - if (m_wait_state != state) - if (!m_wait_line.isnull()) m_wait_line(state? ASSERT_LINE : CLEAR_LINE); - m_wait_state = state; -} - -/* - Acquire the next word as an instruction. The program counter advances by - one word. -*/ -void tms99xx_device::decode(UINT16 inst) -{ - int index = 0; - lookup_entry* table = m_command_lookup_table; - UINT16 opcode = inst; - bool complete = false; - const tms_instruction *decoded; - - m_state = 0; - IR = inst; - m_get_destination = false; - m_byteop = false; - - while (!complete) - { - index = (opcode >> 12) & 0x000f; - if (TRACE_MICRO) logerror("tms99xx: Check next hex digit of instruction %x\n", index); - if (table[index].next_digit != NULL) - { - table = table[index].next_digit; - opcode = opcode << 4; - } - else complete = true; - } - decoded = table[index].entry; - if (decoded == NULL) - { - // not found - logerror("tms99xx: Illegal opcode %04x\n", inst); - IR = 0; - // This will cause another instruction acquisition in the next machine cycle - // with an asserted IAQ line (can be used to indicate this illegal opcode detection). - m_program = NULL; - } - else - { - m_program = decoded->prog; - MPC = -1; - m_command = decoded->id; - if (TRACE_MICRO) logerror("tms99xx: Command decoded as id %d, %s, base opcode %04x\n", m_command, opname[m_command], decoded->opcode); - // Byte operations are either format 1 with the byte flag set - // or format 4 (CRU multi bit operations) with 1-8 bits to transfer. - m_byteop = ((decoded->format==1 && ((IR & 0x1000)!=0)) - || (decoded->format==4 && (((IR >> 6)&0x000f) > 0) && (((IR >> 6)&0x000f) > 9))); - } - m_pass = 1; -} - -inline bool tms99xx_device::byte_operation() -{ - return (IR & 0x1000)!=0; -} - -void tms99xx_device::acquire_instruction() -{ - if (m_mem_phase == 1) - { - if (!m_iaq_line.isnull()) m_iaq_line(ASSERT_LINE); - m_address = PC; - m_first_cycle = m_icount; - } - - mem_read(); - - if (m_mem_phase == 1) - { - decode(m_current_value); - if (TRACE_EXEC) logerror("tms99xx: %04x: %04x (%s)\n", PC, IR, opname[m_command]); - debugger_instruction_hook(this, PC); - PC = (PC + 2) & 0xfffe & m_prgaddr_mask; - // IAQ will be cleared in the main loop - } -} - -/* - Memory read - Clock cycles: 2 + W, W = number of wait states -*/ -void tms99xx_device::mem_read() -{ - // After set_address, any device attached to the address bus may pull down - // READY in order to put the CPU into wait state before the read_word - // operation will be performed - // set_address and read_word should pass the same address as argument - if (m_mem_phase==1) - { - if (!m_dbin_line.isnull()) m_dbin_line(ASSERT_LINE); - m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); - m_check_ready = true; - m_mem_phase = 2; - m_pass = 2; - if (TRACE_ADDRESSBUS) logerror("tms99xx: set address (r) %04x\n", m_address); - - pulse_clock(1); // Concludes the first cycle - // If READY has been found to be low, the CPU will now stay in the wait state loop - } - else - { - // Second phase (after READY was raised again) - m_current_value = m_prgspace->read_word(m_address & m_prgaddr_mask & 0xfffe); - pulse_clock(1); - if (!m_dbin_line.isnull()) m_dbin_line(CLEAR_LINE); - m_mem_phase = 1; // reset to phase 1 - if (TRACE_MEM) logerror("tms99xx: mem r %04x -> %04x\n", m_address, m_current_value); - } -} - -void tms99xx_device::mem_write() -{ - if (m_mem_phase==1) - { - if (!m_dbin_line.isnull()) m_dbin_line(CLEAR_LINE); - // When writing, the data bus is asserted immediately after the address bus - if (TRACE_ADDRESSBUS) logerror("tms99xx: set address (w) %04x\n", m_address); - m_prgspace->set_address(m_address & m_prgaddr_mask & 0xfffe); - if (TRACE_MEM) logerror("tms99xx: mem w %04x <- %04x\n", m_address, m_current_value); - m_prgspace->write_word(m_address & m_prgaddr_mask & 0xfffe, m_current_value); - m_check_ready = true; - m_mem_phase = 2; - m_pass = 2; - pulse_clock(1); - } - else - { - // Second phase (we arrive here when the wait states are over) - pulse_clock(1); - } -} - -void tms99xx_device::register_read() -{ - // Need to set m_address for F1/F3 (we don't know what the data_derive did) - if (m_mem_phase==1) - { - m_address = WP + (m_regnumber<<1); - } - - mem_read(); - - if (m_mem_phase==1) - { - m_register_contents = m_current_value; - } -} - -/* - Memory write: - - Clock cycles: 2 + W, W = number of wait states -*/ -void tms99xx_device::register_write() -{ - // This will be called twice; m_pass is set by the embedded mem_write - UINT16 addr_save = m_address; - m_address = (WP + (m_regnumber<<1)) & m_prgaddr_mask & 0xfffe; - mem_write(); - m_address = addr_save; -} - -/* - CRU support code - - The CRU bus is a 1-bit-wide I/O bus. The CPU can read or write bits at random address. - Special instructions are dedicated to reading and writing one or several consecutive bits. - - The CRU uses the same address bus as the normal memory access. For writing, - the CRUCLK line is pulsed, but not for reading where CRUCLK stays cleared. - This means that each normal memory access also causes read accesses on the - CRU side. The /MEMEN line may be used to distinguish the kinds of accesses - as it stays cleared during CRU operations. - - We do not emulate this here as it seems there are no real applications of - this side effect. Real designs must ensure that CRU read operations are - idempotent (i.e. they must not change the state of the queried device). - - Read returns the number of consecutive CRU bits, with increasing CRU address - from the least significant to the most significant bit; right-aligned - - There seems to be no handling of wait states during CRU operations on the - TMS9900. The TMS9995, in contrast, respects wait states during the transmission - of each single bit. - - Usage of this method: - CRU write: First bit is at rightmost position of m_value. -*/ - -void tms99xx_device::cru_input_operation() -{ - int value, value1; - int offset, location; - - location = (m_cru_address >> 4) & (m_cruaddr_mask>>3); - offset = (m_cru_address>>1) & 0x07; - - // Read 8 bits (containing the desired bits) - value = m_cru->read_byte(location); - - if ((offset + m_count) > 8) // spans two 8 bit cluster - { - // Read next 8 bits - location = (location + 1) & (m_cruaddr_mask>>3); - value1 = m_cru->read_byte(location); - value |= (value1 << 8); - - if ((offset + m_count) > 16) // spans three 8 bit cluster - { - // Read next 8 bits - location = (location + 1) & (m_cruaddr_mask>>3); - value1 = m_cru->read_byte(location); - value |= (value1 << 16); - } - } - - // On each machine cycle (2 clocks) only one CRU bit is transmitted - pulse_clock(m_count<<1); - - // Shift back the bits so that the first bit is at the rightmost place - m_value = (value >> offset); - - // Mask out what we want - m_value &= (0x0000ffff >> (16-m_count)); -} - -void tms99xx_device::cru_output_operation() -{ - int value; - int location; - location = (m_cru_address >> 1) & m_cruaddr_mask; - value = m_value; - - // Write m_count bits from cru_address - for (int i=0; i < m_count; i++) - { - if (TRACE_CRU) logerror("tms99xx: CRU output operation, address %04x, value %d\n", location<<1, value & 0x01); - m_cru->write_byte(location, (value & 0x01)); - value >>= 1; - location = (location + 1) & m_cruaddr_mask; - pulse_clock(2); - } -} - -void tms99xx_device::return_from_subprogram() -{ - // Return from data derivation - // The result should be in m_current_value - // and the address in m_address - m_program = m_caller; - MPC = m_caller_MPC; // will be increased on return -} - -void tms99xx_device::command_completed() -{ - // Pseudo state at the end of the current instruction cycle sequence - if (TRACE_CYCLES) - { - logerror("tms99xx: ------"); - int cycles = m_first_cycle - m_icount; - // Avoid nonsense values due to expired and resumed main loop - if (cycles > 0 && cycles < 10000) logerror(" %d cycles", cycles); - logerror("\n"); - } - m_program = NULL; -} - -/* - This is a switch to a subprogram; there is only one, the data - derivation. In terms of cycles, it does not take any time; execution - continues with the first instruction of the subprogram. -*/ -void tms99xx_device::data_derivation_subprogram() -{ - UINT16 ircopy = IR; - - // Save the return program and position - m_caller = m_program; - m_caller_MPC = MPC; - - // Source or destination argument? - if (m_get_destination) ircopy >>= 6; - - m_regnumber = ircopy & 0x000f; - - m_program = (UINT8*)data_derivation; - MPC = ircopy & 0x0030; - - if (((MPC == 0x0020) && (m_regnumber != 0)) // indexed - || ((MPC == 0x0030) && m_byteop)) // byte operation - { - MPC += 8; // the second option - } - m_get_destination = true; // when we call this the second time before END it's the destination - m_pass = 2; -} - - -/************************************************************************** - Status bit operations -**************************************************************************/ - -inline void tms99xx_device::set_status_bit(int bit, bool state) -{ - if (state) ST |= bit; - else ST &= ~bit; -} - -void tms99xx_device::set_status_parity(UINT8 value) -{ - int count = 0; - for (int i=0; i < 8; i++) - { - if ((value & 0x80)!=0) count++; - value <<= 1; - } - set_status_bit(ST_OP, (count & 1)!=0); -} - -inline void tms99xx_device::compare_and_set_lae(UINT16 value1, UINT16 value2) -{ - set_status_bit(ST_EQ, value1 == value2); - set_status_bit(ST_LH, value1 > value2); - set_status_bit(ST_AGT, (INT16)value1 > (INT16)value2); - if (TRACE_STATUS) logerror("tms99xx: ST = %04x (val1=%04x, val2=%04x)\n", ST, value1, value2); -} - -/************************************************************************** - ALU operations -**************************************************************************/ - -void tms99xx_device::alu_nop() -{ - // Do nothing (or nothing that is externally visible) - pulse_clock(2); - return; -} - -void tms99xx_device::alu_source() -{ - // Copy the current value into the source data register - m_source_even = ((m_address & 1)==0); - m_source_value = m_current_value; - m_source_address = m_address; - pulse_clock(2); -} - -void tms99xx_device::alu_clear() -{ - // Clears the register contents - m_register_contents = 0; - pulse_clock(2); -} - -void tms99xx_device::alu_setaddr() -{ - // Load the current value into the address register - m_address = m_current_value; - pulse_clock(2); -} - -void tms99xx_device::alu_addone() -{ - m_current_value++; - pulse_clock(2); -} - -void tms99xx_device::alu_setaddr_addone() -{ - // Set the address register and increase the recent value - m_address = m_current_value; - m_current_value++; - pulse_clock(2); -} - -void tms99xx_device::alu_pcaddr_advance() -{ - // Set PC as new read address, increase by 2 - m_address = PC; - PC = (PC + 2) & 0xfffe & m_prgaddr_mask; - pulse_clock(2); -} - -void tms99xx_device::alu_add_register() -{ - // Add the register contents to the current value and set as address - m_address = m_current_value + m_register_contents; - pulse_clock(2); -} - -void tms99xx_device::alu_imm() -{ - m_value_copy = m_current_value; - m_address_copy = m_address; - m_address = PC; - PC = (PC + 2) & 0xfffe & m_prgaddr_mask; - pulse_clock(2); -} - -void tms99xx_device::alu_reg() -{ - m_address = (WP + ((IR & 0x000f)<<1)) & m_prgaddr_mask; - pulse_clock(2); -} - -void tms99xx_device::alu_f1() -{ - UINT32 dest_new = 0; - - // Save the destination value - UINT16 prev_dest_value = m_current_value; - - m_destination_even = ((m_address & 1)==0); // this is the destination address; the source address has already been saved - bool byteop = byte_operation(); - - if (byteop) - { - if (!m_destination_even) m_current_value <<= 8; - if (!m_source_even) m_source_value <<= 8; - // We have to strip away the low byte, or byte operations may fail - // e.g. 0x10ff + 0x0101 = 0x1200 - // or 0x2000 - 0x0101 = 0x1eff - m_source_value &= 0xff00; - m_current_value &= 0xff00; - } - - switch (m_command) - { - case A: - case AB: - // Add the contents of the source data to the destination data - // May exceed 0xffff (for carry check) - dest_new = m_current_value + m_source_value; - - // 1000 + e000 = f000 (L) - // c000 + c000 = 8000 (LC) - // 7000 + 4000 = b000 (LO) - // 2000 + f000 = 1000 (LAC) - // c000 + b000 = 7000 (LACO) - // 2000 + e000 = 0000 (EC) - // 8000 + 8000 = 0000 (ECO) - - // When adding, a carry occurs when we exceed the 0xffff value. - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - // If the result has a sign bit that is different from both arguments, we have an overflow - // (i.e. getting a negative value from two positive values and vice versa) - set_status_bit(ST_OV, ((dest_new ^ m_current_value) & (dest_new ^ m_source_value) & 0x8000)!=0); - break; - - case S: - case SB: - // Subtract the contents of the source data from the destination data - dest_new = m_current_value + ((~m_source_value) & 0xffff) + 1; - // LAECO(P) - // 8000 - 8000 = 0000 (EC) - // 2000 - 8000 = a000 (LO) - // 8000 - 2000 = 6000 (LACO) - // 2000 - 1000 = 1000 (LAC) - // 1000 - 2000 = f000 (L) - // 1000 - 1000 = 0000 (EC) - // 1000 - f000 = 2000 (LA) - // f000 - 2000 = d000 (LC) - - // Subtraction means adding the 2s complement, so the carry bit - // is set whenever adding the 2s complement exceeds ffff - // In fact the CPU adds the one's complement, then adds a one. This - // explains why subtracting 0 sets the carry bit. - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - - // If the arguments have different sign bits and the result has a - // sign bit different from the destination value, we have an overflow - // e.g. value1 = 0x7fff, value2 = 0xffff; value1-value2 = 0x8000 - // or value1 = 0x8000, value2 = 0x0001; value1-value2 = 0x7fff - // value1 is the destination value - set_status_bit(ST_OV, (m_current_value ^ m_source_value) & (m_current_value ^ dest_new) & 0x8000); - break; - - case SOC: - case SOCB: - // OR the contents of the source data on the destination data - dest_new = m_current_value | m_source_value; - break; - - case SZC: - case SZCB: - // AND the one's complement of the contents of the source data on the destination data - dest_new = m_current_value & ~m_source_value; - break; - - case MOV: - case MOVB: - // Copy the source data to the destination data - dest_new = m_source_value; - break; - } - - if (byteop) - { - set_status_parity((UINT8)(dest_new>>8)); - - // destnew is the new value to be written (high byte); needs to be - // merged with the existing word - if (m_destination_even) - m_current_value = (prev_dest_value & 0x00ff) | (dest_new & 0xff00); - else - m_current_value = (prev_dest_value & 0xff00) | ((dest_new >> 8) & 0x00ff); - compare_and_set_lae((UINT16)(dest_new & 0xff00), 0); - } - else - { - m_current_value = (UINT16)(dest_new & 0xffff); - compare_and_set_lae((UINT16)(dest_new & 0xffff), 0); - } - - pulse_clock(2); -} - -void tms99xx_device::alu_comp() -{ - m_destination_even = ((m_address & 1)==0); // this is the destination address; the source address has already been saved - if (byte_operation()) - { - if (!m_destination_even) m_current_value <<= 8; - if (!m_source_even) m_source_value <<= 8; - set_status_parity((UINT8)(m_source_value>>8)); - compare_and_set_lae(m_source_value & 0xff00, m_current_value & 0xff00); - } - else - compare_and_set_lae(m_source_value, m_current_value); - - pulse_clock(2); -} - -void tms99xx_device::alu_f3() -{ - switch (m_state) - { - case 0: - // Get register address - m_address = WP + ((IR >> 5) & 0x001e); - m_source_value = m_current_value; - break; - case 1: - if (m_command == COC) - { - set_status_bit(ST_EQ, (m_current_value & m_source_value) == m_source_value); - } - else - { - if (m_command == CZC) - { - set_status_bit(ST_EQ, (~m_current_value & m_source_value) == m_source_value); - } - else - { - // XOR - // The workspace register address is still in m_address - m_current_value = (m_current_value ^ m_source_value); - compare_and_set_lae(m_current_value, 0); - } - } - if (TRACE_STATUS) logerror("tms99xx: ST = %04x\n", ST); - break; - } - - m_state++; - pulse_clock(2); -} - -void tms99xx_device::alu_multiply() -{ - UINT32 result = 0; - - switch (m_state) - { - case 0: // After data derivation - m_source_value = m_current_value; - m_address = ((IR >> 5) & 0x001e) + WP; - break; - case 1: // After reading the register (multiplier) - if (TRACE_ALU) logerror("tms99xx: Multiply %04x by %04x\n", m_current_value, m_source_value); - result = (m_source_value & 0x0000ffff) * (m_current_value & 0x0000ffff); - m_current_value = (result >> 16) & 0xffff; - m_value_copy = result & 0xffff; - pulse_clock(34); // add 36 clock cycles (18 machine cycles); last one in main loop - break; - case 2: // After writing the high word to the destination register - m_current_value = m_value_copy; // Prepare to save low word - m_address = (m_address + 2) & m_prgaddr_mask; - break; - } - pulse_clock(2); - m_state++; -} - -void tms99xx_device::alu_divide() -{ - // Format is DIV Divisor,REG(dividend) - UINT32 uval32; - bool overflow = true; - UINT16 value1; - - switch (m_state) - { - case 0: - m_source_value = m_current_value; // store divisor - // Set address of register - m_address = WP + ((IR >> 5) & 0x001e); - m_address_copy = m_address; - break; - case 1: - // We have an overflow when the quotient cannot be stored in 16 bits - // This is the case when the dividend / divisor >= 0x10000, - // or equivalently, dividend / 0x10000 >= divisor - - if (m_current_value < m_source_value) // also if source=0 - { - MPC++; // skip the abort - overflow = false; - } - set_status_bit(ST_OV, overflow); - m_value_copy = m_current_value; // Save the high word - m_address = (m_address + 2) & m_prgaddr_mask; // Read next word - break; - case 2: - // W2 is in m_current_value - // Create full word and perform division - uval32 = (m_value_copy << 16) | m_current_value; - - if (TRACE_ALU) logerror("tms99xx: Dividing %08x by %04x\n", uval32, m_source_value); - m_current_value = uval32 / m_source_value; - m_value_copy = uval32 % m_source_value; - - if (TRACE_ALU) logerror("tms99xx: Quotient %04x, remainder %04x\n", m_current_value, m_value_copy); - - m_address = m_address_copy; - - // The number of ALU cycles depends on the number of steps in - // the division algorithm. The number of cycles is between 32 and - // 48 (*2 for clock cycles) - // As I don't have a description of the actual algorithm, I'll use - // the following heuristic: We use 32 ALU cycles in general, then - // we need as many cycles as it takes to - // shift away the dividend. Thus, bigger dividends need more cycles. - - pulse_clock(62); // one pulse is at the start, one at the end - value1 = m_value_copy & 0xffff; - - while (value1 != 0) - { - value1 = (value1 >> 1) & 0xffff; - pulse_clock(2); - } - // We still have m_regnumber; this is where m_current_value will go to - break; - case 3: - // Prepare to write the remainder - m_current_value = m_value_copy; - m_address = m_address + 2; - if (TRACE_STATUS) logerror("tms99xx: ST = %04x (div)\n", ST); - break; - } - pulse_clock(2); - m_state++; -} - -void tms99xx_device::alu_xop() -{ - switch (m_state) - { - case 0: - // We have the effective address of the source operand in m_address - m_address_saved = m_address; - // Now we take the XOP number from the instruction register - // and calculate the vector location - // [0010 11xx xx tt SSSS] shift 6 right, then *4 => shift 4 right - m_address = 0x0040 + ((IR >> 4) & 0x003c); - // Takes some additional cycles - pulse_clock(4); - break; - case 1: - m_value_copy = WP; // save the old WP - WP = m_current_value & m_prgaddr_mask & 0xfffe; // the new WP has been read in the previous microoperation - m_current_value = m_address_saved; // we saved the address of the source operand; retrieve it - m_address = WP + 0x0016; // Next register is R11 - break; - case 2: - m_address = WP + 0x001e; - m_current_value = ST; - break; - case 3: - m_address = WP + 0x001c; - m_current_value = PC; - break; - case 4: - m_address = WP + 0x001a; - m_current_value = m_value_copy; // old WP into new R13 - break; - case 5: - m_address = 0x0042 + ((IR >> 4) & 0x003c); // location of new PC - set_status_bit(ST_X, true); - break; - case 6: - PC = m_current_value & m_prgaddr_mask & 0xfffe; - break; - } - pulse_clock(2); - m_state++; -} - -void tms99xx_device::alu_clr_swpb() -{ - UINT32 dest_new = 0; - UINT32 src_val = m_current_value & 0x0000ffff; - UINT16 sign = 0; - - bool setstatus = true; - bool check_ov = true; - - switch (m_command) - { - case CLR: - // no status bits - m_current_value = 0x0000; - setstatus = false; - break; - case SETO: - // no status bits - m_current_value = 0xffff; - setstatus = false; - break; - case INV: - // LAE - dest_new = ~src_val & 0xffff; - check_ov = false; - break; - case NEG: - // LAECO - // Overflow occurs for value=0x8000 - dest_new = ((~src_val) & 0x0000ffff) + 1; - check_ov = false; - set_status_bit(ST_OV, src_val == 0x8000); - break; - case INC: - // LAECO - // Overflow for result value = 0x8000 - // Carry for result value = 0x0000 - dest_new = src_val + 1; - break; - case INCT: - // LAECO - // Overflow for result value = 0x8000 / 0x8001 - // Carry for result value = 0x0000 / 0x0001 - dest_new = src_val + 2; - break; - case DEC: - // LAECO - // Carry for result value != 0xffff - // Overflow for result value == 0x7fff - dest_new = src_val + 0xffff; - sign = 0x8000; - break; - case DECT: - // Carry for result value != 0xffff / 0xfffe - // Overflow for result value = 0x7fff / 0x7ffe - dest_new = src_val + 0xfffe; - sign = 0x8000; - break; - case SWPB: - m_current_value = ((m_current_value << 8) | (m_current_value >> 8)) & 0xffff; - setstatus = false; - break; - } - - if (setstatus) - { - if (check_ov) set_status_bit(ST_OV, ((src_val & 0x8000)==sign) && ((dest_new & 0x8000)!=sign)); - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - m_current_value = dest_new & 0xffff; - compare_and_set_lae(m_current_value, 0); - } - - pulse_clock(2); - // No states here -} - -void tms99xx_device::alu_abs() -{ - // LAECO (from original word!) - // O if >8000 - // C is alwas reset - set_status_bit(ST_OV, m_current_value == 0x8000); - set_status_bit(ST_C, false); - compare_and_set_lae(m_current_value, 0); - - if ((m_current_value & 0x8000)!=0) - { - m_current_value = (((~m_current_value) & 0x0000ffff) + 1) & 0xffff; - pulse_clock(2); // If ABS is performed it takes one machine cycle more - } - else - { - MPC++; // skips over the next micro operation (MEMORY_WRITE) - } - pulse_clock(2); -} - -void tms99xx_device::alu_x() -{ - if (TRACE_ALU) logerror("tms99xx: Substituting current command by %04x\n", m_current_value); - decode(m_current_value); - pulse_clock(2); -} - -/* - Also used by other microprograms -*/ -void tms99xx_device::alu_b() -{ - // no status bits - // Although we got the contents of the source data, we do not use them - // but directly branch there. That is, we are only interested in the - // address of the source data. - // If we have a B *R5 and R5 contains the value 0xa000, the CPU actually - // retrieves the value at 0xa000, but in fact it will load the PC - // with the address 0xa000 - m_current_value = PC; - PC = m_address & m_prgaddr_mask & 0xfffe; - m_address = WP + 22; - if (TRACE_ALU) logerror("tms99xx: Set new PC = %04x\n", PC); - pulse_clock(2); -} - -void tms99xx_device::alu_blwp() -{ - switch (m_state) - { - case 0: - m_value_copy = WP; - WP = m_current_value & m_prgaddr_mask & 0xfffe; // set new WP (*m_destination) - m_address_saved = (m_address + 2) & m_prgaddr_mask; // Save the location of the WP - m_address = WP + 30; - m_current_value = ST; // get status register - break; - case 1: - m_current_value = PC; // get program counter - m_address = m_address - 2; - break; - case 2: - m_current_value = m_value_copy; // retrieve the old WP - m_address = m_address - 2; - break; - case 3: - m_address = m_address_saved; // point to PC component of branch vector - break; - case 4: - PC = m_current_value & m_prgaddr_mask & 0xfffe; - if (TRACE_ALU) logerror("tms9900: Context switch complete; WP=%04x, PC=%04x, ST=%04x\n", WP, PC, ST); - break; - } - pulse_clock(2); - m_state++; -} - -void tms99xx_device::alu_ldcr() -{ - UINT16 value; - - // Spec: "If the source operand address is odd, the address is truncated - // to an even address prior to data transfer." - // (Editor/Assembler, page 151) - // This refers to transfers with more than 8 bits. In this case, for - // LDCR the first bit is taken from the least significant bit of the - // source word. If the address is odd (e.g. 0x1001), it is - // treated as 0x1000, that is, truncated to an even address. - // For transfers with 1-8 bits, the first bit is the least significant - // bit of the source byte (any address). - - if (m_state == 0) - { - m_address = WP + 24; - } - else - { - value = m_source_value; // copied by ALU_SOURCE - m_count = (IR >> 6) & 0x000f; - if (m_count == 0) m_count = 16; - if (m_count <= 8) - { - if (m_source_even) value>>=8; - set_status_parity((UINT8)(value & 0xff)); - compare_and_set_lae(value<<8, 0); - } - else - { - compare_and_set_lae(value, 0); - } - m_cru_address = m_current_value; - m_value = value; - if (TRACE_CRU) logerror("tms99xx: Load CRU address %04x (%d bits), value = %04x\n", m_cru_address, m_count, m_value); - } - m_state++; - pulse_clock(2); -} - -void tms99xx_device::alu_stcr() -{ - UINT16 value; - int n = 2; - // For STCR transfers with more than 8 bits, the first CRU bit is - // always put into the least significant bit of the destination word. - // If the address is odd (e.g. 0x1001), it is treated as 0x1000, that is, - // truncated to an even boundary. - // For transfers with 1-8 bits, the destination address is handled as - // in MOVB operations, i.e. the other byte of the word is kept unchanged. - - switch (m_state) - { - case 0: // After getting the destination operand and saving the address/value - m_address = WP + 24; - n = 0; - break; - case 1: // After getting R12 - m_cru_address = m_current_value; - m_count = (IR >> 6) & 0x000f; - if (m_count == 0) m_count = 16; - break; - case 2: // After the cru operation; value starts at LSB of m_value - value = m_value & 0xffff; - if (m_count < 9) - { - if (TRACE_CRU) logerror("tms99xx: Store CRU at %04x (%d bits) in %04x, result = %02x\n", m_cru_address, m_count, m_source_address, value); - set_status_parity((UINT8)(value & 0xff)); - compare_and_set_lae(value<<8, 0); - if (m_source_even) - m_current_value = (m_source_value & 0x00ff) | (value<<8); - else - m_current_value = (m_source_value & 0xff00) | (value & 0xff); - - pulse_clock(2*(5 + (8-m_count))); - } - else - { - if (TRACE_CRU) logerror("tms99xx: Store CRU at %04x (%d bits) in %04x, result = %04x\n", m_cru_address, m_count, m_source_address, value); - m_current_value = value; - compare_and_set_lae(value, 0); - pulse_clock(2*(5 + (16-m_count))); - } - m_address = m_source_address; - break; - } - - m_state++; - pulse_clock(n); -} - -void tms99xx_device::alu_sbz_sbo() -{ - INT8 displacement; - if (m_state==0) - { - m_address = WP + 24; - } - else - { - m_value = (m_command==SBO)? 1 : 0; - displacement = (INT8)(IR & 0xff); - m_cru_address = m_current_value + (displacement<<1); - m_count = 1; - } - m_state++; - pulse_clock(2); -} - -void tms99xx_device::alu_tb() -{ - INT8 displacement; - switch (m_state) - { - case 0: - m_address = WP + 24; - break; - case 1: - displacement = (INT8)(IR & 0xff); - m_cru_address = m_current_value + (displacement<<1); - m_count = 1; - break; - case 2: - set_status_bit(ST_EQ, m_value!=0); - if (TRACE_STATUS) logerror("tms99xx: ST = %04x\n", ST); - break; - } - m_state++; - pulse_clock(2); -} - -void tms99xx_device::alu_jmp() -{ - INT8 displacement; - bool cond = false; - - if (m_state==0) - { - switch (m_command) - { - case JMP: - cond = true; - break; - case JLT: // LAECOP == x00xxx - cond = ((ST & (ST_AGT | ST_EQ))==0); - break; - case JLE: // LAECOP == 0xxxxx - cond = ((ST & ST_LH)==0); - break; - case JEQ: // LAECOP == xx1xxx - cond = ((ST & ST_EQ)!=0); - break; - case JHE: // LAECOP == 1x0xxx, 0x1xxx - cond = ((ST & (ST_LH | ST_EQ)) != 0); - break; - case JGT: // LAECOP == x1xxxx - cond = ((ST & ST_AGT)!=0); - break; - case JNE: // LAECOP == xx0xxx - cond = ((ST & ST_EQ)==0); - break; - case JNC: // LAECOP == xxx0xx - cond = ((ST & ST_C)==0); - break; - case JOC: // LAECOP == xxx1xx - cond = ((ST & ST_C)!=0); - break; - case JNO: // LAECOP == xxxx0x - cond = ((ST & ST_OV)==0); - break; - case JL: // LAECOP == 0x0xxx - cond = ((ST & (ST_LH | ST_EQ)) == 0); - break; - case JH: // LAECOP == 1xxxxx - cond = ((ST & ST_LH)!=0); - break; - case JOP: // LAECOP == xxxxx1 - cond = ((ST & ST_OP)!=0); - break; - } - if (!cond) - { - if (TRACE_ALU) logerror("tms99xx: Jump condition false\n"); - MPC+=1; // skip next ALU call - } - else - if (TRACE_ALU) logerror("tms99xx: Jump condition true\n"); - } - else - { - displacement = (IR & 0xff); - PC = (PC + (displacement<<1)) & m_prgaddr_mask & 0xfffe; - } - m_state++; - pulse_clock(2); -} - -void tms99xx_device::alu_shift() -{ - bool carry = false; - bool overflow = false; - UINT16 sign = 0; - UINT32 value; - int count; - - switch (m_state) - { - case 0: - m_address = WP + ((IR & 0x000f)<<1); - pulse_clock(2); - break; - case 1: - // we have the value of the register in m_current_value - // Save it (we may have to read R0) - m_value_copy = m_current_value; - m_address_saved = m_address; - m_address = WP; - m_current_value = (IR >> 4) & 0x000f; - - if (m_current_value != 0) - { - // skip the next read and ALU operation - MPC = MPC+2; - m_state++; - } - else - { - if (TRACE_ALU) logerror("tms99xx: Shift operation gets count from R0\n"); - pulse_clock(2); - } - pulse_clock(2); - break; - case 2: - // after READ - pulse_clock(2); - pulse_clock(2); - break; - case 3: - count = m_current_value & 0x000f; // from the instruction or from R0 - if (count==0) count = 16; - - value = m_value_copy; - - // we are re-implementing the shift operations because we have to pulse - // the clock at each single shift anyway. - // Also, it is easier to implement the status bit setting. - // Note that count is never 0 - if (m_command == SRA) sign = value & 0x8000; - - for (int i=0; i < count; i++) - { - switch (m_command) - { - case SRL: - case SRA: - carry = ((value & 1)!=0); - value = (value >> 1) | sign; - break; - case SLA: - carry = ((value & 0x8000)!=0); - value <<= 1; - if (carry != ((value&0x8000)!=0)) overflow = true; - break; - case SRC: - carry = ((value & 1)!=0); - value = (value>>1) | (carry? 0x8000 : 0x0000); - break; - } - pulse_clock(2); - } - - m_current_value = value & 0xffff; - set_status_bit(ST_C, carry); - set_status_bit(ST_OV, overflow); - compare_and_set_lae(m_current_value, 0); - m_address = m_address_saved; // Register address - if (TRACE_STATUS) logerror("tms99xx: ST = %04x (val=%04x)\n", ST, m_current_value); - break; - } - m_state++; -} - -void tms99xx_device::alu_ai_ori() -{ - UINT32 dest_new = 0; - switch (m_command) - { - case AI: - dest_new = m_current_value + m_value_copy; - // See status bit handling for Add - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - set_status_bit(ST_OV, ((dest_new ^ m_current_value) & (dest_new ^ m_value_copy) & 0x8000)!=0); - break; - case ANDI: - dest_new = m_current_value & m_value_copy; - break; - case ORI: - dest_new = m_current_value | m_value_copy; - break; - } - m_current_value = dest_new & 0xffff; - m_address = m_address_copy; - compare_and_set_lae(m_current_value, 0); - pulse_clock(2); -} - -void tms99xx_device::alu_ci() -{ - compare_and_set_lae(m_value_copy, m_current_value); - pulse_clock(2); -} - -void tms99xx_device::alu_li() -{ - compare_and_set_lae(m_current_value, 0); - pulse_clock(2); -} - -void tms99xx_device::alu_lwpi() -{ - WP = m_current_value & m_prgaddr_mask & 0xfffe; - pulse_clock(2); -} - -void tms99xx_device::alu_limi() -{ - ST = (ST & 0xfff0) | (m_current_value & 0x000f); - if (TRACE_STATUS) logerror("tms99xx: ST = %04x\n", ST); - pulse_clock(2); -} - -void tms99xx_device::alu_stwp_stst() -{ - if (m_command==STST) m_current_value = ST; - else m_current_value = WP; - pulse_clock(2); -} - -void tms99xx_device::alu_external() -{ - // Call some possibly attached external device - // We pass the bit pattern of the address bus to the external function - - // IDLE = 0000 0011 0100 0000 - // RSET = 0000 0011 0110 0000 - // CKON = 0000 0011 1010 0000 - // CKOF = 0000 0011 1100 0000 - // LREX = 0000 0011 1110 0000 - // --- - if (m_command == IDLE) - m_idle_state = true; - - if (!m_external_operation.isnull()) m_external_operation((IR >> 5) & 0x07, 1, 0xff); - pulse_clock(2); -} - -void tms99xx_device::alu_rtwp() -{ - switch (m_state) - { - case 0: - m_address = WP + 30; // R15 - pulse_clock(2); - break; - case 1: - ST = m_current_value; - m_address -= 2; // R14 - break; - case 2: - PC = m_current_value & m_prgaddr_mask & 0xfffe; - m_address -= 2; // R13 - break; - case 3: - WP = m_current_value & m_prgaddr_mask & 0xfffe; - pulse_clock(2); - break; - } - m_state++; -} - - -void tms99xx_device::alu_int() -{ - if (TRACE_EMU) logerror("tms99xx: INT state %d; irq_level %d\n", m_state, m_irq_level); - switch (m_state) - { - case 0: - if (m_irq_level == RESET_INT) - { - m_address = 0; - pulse_clock(2); - } - else - { - if (m_irq_level == LOAD_INT) m_address = 0xfffc; // will be truncated for TMS9980 - else - { - m_address = (m_irq_level << 2); - } - } - break; - case 1: - m_address_copy = m_address; - m_value_copy = WP; // old WP - WP = m_current_value & m_prgaddr_mask & 0xfffe; // new WP - m_current_value = ST; - m_address = (WP + 30) & m_prgaddr_mask; - break; - case 2: - m_current_value = PC; - m_address = (WP + 28) & m_prgaddr_mask; - break; - case 3: - m_current_value = m_value_copy; // old WP - m_address = (WP + 26) & m_prgaddr_mask; - break; - case 4: - m_address = (m_address_copy + 2) & 0xfffe & m_prgaddr_mask; - if (TRACE_ALU) logerror("tms99xx: read from %04x\n", m_address); - break; - case 5: - PC = m_current_value & m_prgaddr_mask & 0xfffe; - if (m_irq_level > 0 ) - { - ST = (ST & 0xfff0) | (m_irq_level - 1); - } - break; - } - m_state++; - pulse_clock(2); -} - -/**************************************************************************/ -UINT32 tms99xx_device::execute_min_cycles() const -{ - return 2; -} - -// TODO: Compute this value, just a wild guess for the average -UINT32 tms99xx_device::execute_max_cycles() const -{ - return 10; -} - -UINT32 tms99xx_device::execute_input_lines() const -{ - return 2; -} - -// clocks to cycles, cycles to clocks = id -// execute_default_irq_vector = 0 -// execute_burn = nop - -// device_disasm_interface overrides -UINT32 tms99xx_device::disasm_min_opcode_bytes() const -{ - return 2; -} - -UINT32 tms99xx_device::disasm_max_opcode_bytes() const -{ - return 6; -} - -offs_t tms99xx_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) -{ - extern CPU_DISASSEMBLE( tms9900 ); - return CPU_DISASSEMBLE_NAME(tms9900)(this, buffer, pc, oprom, opram, options); -} - - -const device_type TMS9900 = &device_creator<tms9900_device>; diff --git a/src/emu/cpu/tms9900/tms9900.h b/src/emu/cpu/tms9900/tms9900.h deleted file mode 100644 index 8e7f19042c6..00000000000 --- a/src/emu/cpu/tms9900/tms9900.h +++ /dev/null @@ -1,403 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - TMS9900 processor - This is a re-implementation of the TMS9900 featuring a cycle-precise - behaviour. - - See tms9900.c for documentation -*/ - -#ifndef __TMS9900_H__ -#define __TMS9900_H__ - -#include "emu.h" -#include "debugger.h" -#include "tms99com.h" - -enum -{ - INT_9900_RESET = 0, - INT_9900_LOAD = 1, - INT_9900_INTREQ = 2 -}; - -enum -{ - LOAD_INT = -1, - RESET_INT = -2 -}; - -static const char opname[][5] = -{ "ILL ", "A ", "AB ", "ABS ", "AI ", "ANDI", "B ", "BL ", "BLWP", "C ", - "CB ", "CI ", "CKOF", "CKON", "CLR ", "COC ", "CZC ", "DEC ", "DECT", "DIV ", - "IDLE", "INC ", "INCT", "INV ", "JEQ ", "JGT ", "JH ", "JHE ", "JL ", "JLE ", - "JLT ", "JMP ", "JNC ", "JNE ", "JNO ", "JOC ", "JOP ", "LDCR", "LI ", "LIMI", - "LREX", "LWPI", "MOV ", "MOVB", "MPY ", "NEG ", "ORI ", "RSET", "RTWP", "S ", - "SB ", "SBO ", "SBZ ", "SETO", "SLA ", "SOC ", "SOCB", "SRA ", "SRC ", "SRL ", - "STCR", "STST", "STWP", "SWPB", "SZC ", "SZCB", "TB ", "X ", "XOP ", "XOR ", - "*int" -}; - -class tms99xx_device : public cpu_device -{ -public: - tms99xx_device(const machine_config &mconfig, device_type type, const char *name, - const char *tag, int databus_width, int prg_addr_bits, int cru_addr_bits, - device_t *owner, UINT32 clock, const char *shortname, const char *source); - - ~tms99xx_device(); - - // READY input line. When asserted (high), the memory is ready for data exchange. - void set_ready(int state); - - // HOLD input line. When asserted (low), the CPU is requested to release the - // data and address bus and enter the HOLD state. The entrance of this state - // is acknowledged by the HOLDA output line. - void set_hold(int state); - - // Callbacks - template<class _Object> static devcb_base &static_set_extop_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_external_operation.set_callback(object); } - template<class _Object> static devcb_base &static_set_intlevel_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_get_intlevel.set_callback(object); } - template<class _Object> static devcb_base &static_set_iaq_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_iaq_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_clkout_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_clock_out_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_wait_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_wait_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_holda_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_holda_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_dbin_callback(device_t &device, _Object object) { return downcast<tms99xx_device &>(device).m_dbin_line.set_callback(object); } - -protected: - // device-level overrides - virtual void device_start(); - virtual void device_stop(); - virtual void device_reset(); - - virtual void resolve_lines(); - - // device_execute_interface overrides - virtual UINT32 execute_min_cycles() const; - virtual UINT32 execute_max_cycles() const; - virtual UINT32 execute_input_lines() const; - virtual void execute_set_input(int irqline, int state); - virtual void execute_run(); - - // device_disasm_interface overrides - virtual UINT32 disasm_min_opcode_bytes() const; - virtual UINT32 disasm_max_opcode_bytes() const; - virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options); - - const address_space_config* memory_space_config(address_spacenum spacenum) const; - - // Let these methods be overloaded by the TMS9980. - virtual void mem_read(void); - virtual void mem_write(void); - virtual void acquire_instruction(void); - void decode(UINT16 inst); - - const address_space_config m_program_config; - const address_space_config m_io_config; - address_space* m_prgspace; - address_space* m_cru; - - virtual UINT16 read_workspace_register_debug(int reg); - virtual void write_workspace_register_debug(int reg, UINT16 data); - - // Cycle counter - int m_icount; - - // TMS9900 hardware registers - UINT16 WP; // Workspace pointer - UINT16 PC; // Program counter - UINT16 ST; // Status register - - // Internal register - UINT16 IR; // Instruction register - - // Stored address - UINT16 m_address; - - // Stores the recently read word or the word to be written - UINT16 m_current_value; - - // Decoded command - UINT16 m_command; - - // Is it a byte operation? Only format 1 commands with the byte flag set - // and CRU commands with less than 9 bits to transfer are byte operations. - bool m_byteop; - - // Issue clock pulses. Note that each machine cycle has two clock cycles. - void pulse_clock(int count); - - // For multi-pass operations. For instance, memory word accesses are - // executed as two consecutive byte accesses. CRU accesses are repeated - // single-bit accesses. (Needed for TMS9980) - int m_pass; - - // Data bus width. Needed for TMS9980. - int m_databus_width; - - // Check the READY line? - bool m_check_ready; - - // Phase of the memory access - int m_mem_phase; - - // Max address - const UINT16 m_prgaddr_mask; - const UINT16 m_cruaddr_mask; - - bool m_load_state; - bool m_irq_state; - bool m_reset; - - // Determine the interrupt level using the IC0-IC3 lines - int get_intlevel(int state); - - // Interrupt level as acquired from input lines (TMS9900: IC0-IC3, TMS9980: IC0-IC2) - // We assume all values right-justified, i.e. TMS9980 also counts up by one - int m_irq_level; - - // Used to display the number of consumed cycles in the log. - int m_first_cycle; - - /************************************************************************/ - - // Clock output. This is not a pin of the TMS9900 because the TMS9900 - // needs an external clock, and usually one of those external lines is - // used for this purpose. - devcb_write_line m_clock_out_line; - - // Wait output. When asserted (high), the CPU is in a wait state. - devcb_write_line m_wait_line; - - // HOLD Acknowledge line. When asserted (high), the CPU is in HOLD state. - devcb_write_line m_holda_line; - - // Signal to the outside world that we are now getting an instruction - devcb_write_line m_iaq_line; - - // Get the value of the interrupt level lines - devcb_read8 m_get_intlevel; - - // DBIN line. When asserted (high), the CPU has disabled the data bus output buffers. - devcb_write_line m_dbin_line; - - // Trigger external operation. This is achieved by putting a special value in - // the most significant three bits of the address bus (TMS9995: data bus) and - // pulsing the CRUCLK line. - // Accordingly, we have - // - // A0 A1 A2 A3 A4 A5 ... A12 A13 A14 A15 - // 0 0 0 x x x x x x - normal CRU access - // 0 1 0 x x x x x x - IDLE - // 0 1 1 x x x x x x - RSET - // 1 0 1 x x x x x x - CKON - // 1 1 0 x x x x x x - CKOF - // 1 1 1 x x x x x x - LREX - // - // so the TMS9900 can only use CRU addresses 0 - 1ffe for CRU operations. - // By moving these three bits to the data bus, the TMS9995 can allow for the - // full range 0000-fffe for its CRU operations. - // - // We could realize this via the CRU access as well, but the data bus access - // is not that simple to emulate. For the sake of homogenity between the - // chip emulations we use a dedicated callback. - devcb_write8 m_external_operation; - - -private: - // Indicates if this is a byte-oriented command - inline bool byte_operation(); - - // Processor states - bool m_idle_state; - - // READY handling. The READY line is operated before the phi1 clock - // pulse rises. As the ready line is only set once in this emulation we - // keep the level in a buffer (like a latch) - bool m_ready_bufd; // buffered state - bool m_ready; // sampled value - - bool m_wait_state; - bool m_hold_state; - - // State / debug management - UINT16 m_state_any; - static const char* s_statename[]; - void state_import(const device_state_entry &entry); - void state_export(const device_state_entry &entry); - void state_string_export(const device_state_entry &entry, std::string &str); - - // Interrupt handling - void service_interrupt(); - - // ================ Microprogram support ======================== - - // Set up lookup table - void build_command_lookup_table(); - - // Sequence of micro-operations - typedef const UINT8* microprogram; - - // Method pointer - typedef void (tms99xx_device::*ophandler)(void); - - // Opcode list entry - struct tms_instruction - { - UINT16 opcode; - int id; - int format; - microprogram prog; // Microprogram - }; - - // Lookup table entry - struct lookup_entry - { - lookup_entry *next_digit; - const tms_instruction *entry; - }; - - // Pointer to the lookup table - lookup_entry* m_command_lookup_table; - - // List of allocated tables (used for easy clean-up on exit) - lookup_entry* m_lotables[32]; - - // List of pointers for micro-operations - static const tms99xx_device::ophandler s_microoperation[]; - - // Opcode table - static const tms99xx_device::tms_instruction s_command[]; - - // Micro-operation declarations - void register_read(void); - void register_write(void); - void cru_input_operation(void); - void cru_output_operation(void); - void data_derivation_subprogram(void); - void return_from_subprogram(void); - void command_completed(void); - - void alu_nop(void); - void alu_clear(void); - void alu_source(void); - void alu_setaddr(void); - void alu_addone(void); - void alu_setaddr_addone(void); - void alu_pcaddr_advance(void); - void alu_add_register(void); - - void alu_imm(void); - void alu_reg(void); - - void alu_f1(void); - void alu_comp(void); - void alu_f3(void); - void alu_multiply(void); - void alu_divide(void); - void alu_xop(void); - void alu_clr_swpb(void); - void alu_abs(void); - void alu_x(void); - void alu_b(void); - void alu_bl(void); - void alu_blwp(void); - void alu_ldcr(void); - void alu_stcr(void); - void alu_sbz_sbo(void); - void alu_tb(void); - void alu_jmp(void); - void alu_shift(void); - void alu_ai_ori(void); - void alu_ci(void); - void alu_li(void); - void alu_lwpi(void); - void alu_limi(void); - void alu_stwp_stst(void); - void alu_external(void); - void alu_rtwp(void); - void alu_int(void); - - void abort_operation(void); - - // Micro-operation - UINT8 m_op; - - // Micro-operation program counter (as opposed to the program counter PC) - int MPC; - - // Current microprogram - const UINT8* m_program; - - // Calling microprogram (used when data derivation is called) - const UINT8* m_caller; - int m_caller_MPC; - - // State of the micro-operation. Needed for repeated ALU calls. - int m_state; - - // Has HOLD been acknowledged yet? - bool m_hold_acknowledged; - - // Signal the wait state via the external line - inline void set_wait_state(bool state); - - // Used to acknowledge HOLD and enter the HOLD state - inline void acknowledge_hold(); - - // Was the source operand a byte from an even address? - bool m_source_even; - - // Was the destination operand a byte from an even address? - bool m_destination_even; - - // Intermediate storage for the source operand - UINT16 m_source_address; - UINT16 m_source_value; - UINT16 m_address_saved; - - // Another copy of the address - UINT16 m_address_copy; - - // Stores the recently read register contents - UINT16 m_register_contents; - - // Stores the register number for the next register access - int m_regnumber; - - // CRU support: Stores the CRU address - UINT16 m_cru_address; - - // CRU support: Stores the number of bits to be transferred - int m_count; - - // Copy of the value - UINT16 m_value_copy; - - // Another internal register, storing intermediate values - // Using 32 bits to support MPY - UINT32 m_value; - - // For two-argument commands. Indicates whether this is the second operand. - bool m_get_destination; - - // Status register update - inline void set_status_bit(int bit, bool state); - inline void compare_and_set_lae(UINT16 value1, UINT16 value2); - void set_status_parity(UINT8 value); -}; - -/*****************************************************************************/ - -class tms9900_device : public tms99xx_device -{ -public: - tms9900_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); -}; - - -// device type definition -extern const device_type TMS9900; - -#endif /* __TMS9900_H__ */ diff --git a/src/emu/cpu/tms9900/tms9980a.c b/src/emu/cpu/tms9900/tms9980a.c deleted file mode 100644 index 054a293a403..00000000000 --- a/src/emu/cpu/tms9900/tms9980a.c +++ /dev/null @@ -1,300 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - Cycle-precise implementation of the TMS9980A. - Subclassed from tms99xx_device in tms9900.c. - - +----------------+ - /HOLD | 1 \/ 40| /MEMEN - HOLDA | 2 39| READY - IAQ | 3 38| /WE - LSB +- A13,CRUOUT | 4 37| CRUCLK - | A12 | 5 36| Vdd - | A11 | 6 35| Vss - | A10 | 7 34| CKIN - Address A9 | 8 33| D7 --+ - bus A8 | 9 32| D6 | - | A7 |10 31| D5 Data - 16KiB A6 |11 30| D4 bus - | A5 |12 29| D3 | - | A4 |13 28| D2 2 * 8 bit - | A3 |14 27| D1 | - | A2 |15 26| D0 --+ - | A1 |16 25| INT0 --+ - MSB +-- A0 |17 24| INT1 | Interrupt levels - DBIN |18 23| INT2 --+ - CRUIN |19 22| /PHI3 - Vcc |20 21| Vbb - +----------------+ - - The TMS9980A is similar to the TMS9900, with the following differences: - - - Address bus is only 14 bit wide (16 KiB) - - Data bus is 16 bit wide and multiplexed on 8 lines (2 bytes per access) - - CRU space is limited to 2048 bits (due to fewer address lines) - - Only three interrupt level lines, for a maximum of 8 levels. - - No INTREQ, RESET, and LOAD lines. All interrupts are signaled via INT0 - - INT2. Reset=00x, Load=010, Level1=011, Level2=100, Level3=101, Level4=110, - all interrupts cleared=111. - - Memory accesses are always 2 bytes (even address byte, odd address byte) - even for byte operations. Thus the 9980A, like the TMS9900, needs to - pre-fetch the word at the destination before overwriting it. - - On the cycle level both TMS9900 and TMS9980A are equal, except for the - additional cycles needed for memory read and write access. Accordingly, - the emulation shares the core and the microprograms and redefines the - memory access and the interrupt handling only. - - The 9980A has the same external instructions as the TMS9900, but it - indicates the command via A0, A1, and A13 (instead of A0-A2). - - For pin definitions see tms9900.c - - Michael Zapf, 2012 -*/ - -#include "tms9980a.h" - -/* - The following defines can be set to 0 or 1 to disable or enable certain - output in the log. -*/ - -// Memory operation -#define TRACE_MEM 0 - -// Address bus operation -#define TRACE_ADDRESSBUS 0 - -// Log operation -#define TRACE_OP 0 - -// Interrupts -#define TRACE_INT 0 - -/**************************************************************************** - Constructor -****************************************************************************/ - -tms9980a_device::tms9980a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms99xx_device(mconfig, TMS9980A, "TMS9980A", tag, 8, 14, 11, owner, clock, "tms9980a", __FILE__) -{ -} - -/* - External connections -*/ -void tms9980a_device::resolve_lines() -{ - // Resolve our external connections - m_external_operation.resolve(); - m_iaq_line.resolve(); - m_clock_out_line.resolve(); - m_holda_line.resolve(); - m_dbin_line.resolve(); -} - -UINT16 tms9980a_device::read_workspace_register_debug(int reg) -{ - int temp = m_icount; - int addr = (WP+(reg<<1)) & 0xfffe & m_prgaddr_mask; - UINT16 value = (m_prgspace->read_byte(addr) << 8) | (m_prgspace->read_byte(addr+1) & 0xff); - m_icount = temp; - return value; -} - -void tms9980a_device::write_workspace_register_debug(int reg, UINT16 data) -{ - int temp = m_icount; - int addr = (WP+(reg<<1)) & 0xfffe & m_prgaddr_mask; - m_prgspace->write_byte(addr, data>>8); - m_prgspace->write_byte(addr+1, data & 0xff); - m_icount = temp; -} - -/* - Interrupt input. Keep in mind that the TMS9980A does not have any INTREQ - line but signals interrupts via IC0-IC2 only. Thus we cannot take down any - single interrupt; only all interrupts can be cleared at once using level 7. - The state parameter is actually not needed. -*/ -void tms9980a_device::execute_set_input(int irqline, int state) -{ - // We model the three lines IC0-IC2 as 8 separate input lines, although we - // cannot assert more than one at a time. The state value is not needed, - // as level 7 means to clean all interrupts, but we consider it for the - // sake of consistency. - - int level = irqline; - - // Just to stay consistent. - if (state==CLEAR_LINE) level = INT_9980A_CLEAR; - - switch (level) - { - case INT_9980A_RESET: - case 1: - level = RESET_INT; - m_reset = true; - break; - case INT_9980A_LOAD: - level = LOAD_INT; - break; - case INT_9980A_LEVEL1: - case INT_9980A_LEVEL2: - case INT_9980A_LEVEL3: - case INT_9980A_LEVEL4: - level = level - 2; - break; - case INT_9980A_CLEAR: - // Clear all interrupts - m_load_state = false; - m_irq_state = false; - if (TRACE_INT) logerror("tms9980a: clear interrupts\n"); - break; - } - - m_irq_level = level; - - if (m_irq_level != INT_9980A_CLEAR) - { - if (m_irq_level == LOAD_INT) - { - // Some boards start up with LOAD interrupt, so we clear the reset flag - m_reset = false; - m_load_state = true; - } - else m_irq_state = true; - if (TRACE_INT) logerror("tms9980a: interrupt level=%d, ST=%04x\n", m_irq_level, ST); - } -} - -/*****************************************************************************/ - -/* - Memory read: - Clock cycles: 4 + 2W, W = number of wait states -*/ -void tms9980a_device::mem_read() -{ - UINT8 value; - switch (m_mem_phase) - { - case 1: - m_pass = 4; // make the CPU visit this method more than once - if (!m_dbin_line.isnull()) m_dbin_line(ASSERT_LINE); - m_prgspace->set_address(m_address & m_prgaddr_mask & ~1); - if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", m_address & m_prgaddr_mask & ~1); - m_check_ready = true; - break; - case 2: - // Sample the value on the data bus (high byte) - value = m_prgspace->read_byte(m_address & m_prgaddr_mask & ~1); - if (TRACE_MEM) logerror("tms9980a: memory read high byte %04x -> %02x\n", m_address & m_prgaddr_mask & ~1, value); - m_current_value = (value << 8) & 0xff00; - break; - case 3: - m_prgspace->set_address((m_address & m_prgaddr_mask) | 1); - if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", (m_address & m_prgaddr_mask) | 1); - break; - case 4: - // Sample the value on the data bus (low byte) - value = m_prgspace->read_byte((m_address & m_prgaddr_mask) | 1); - m_current_value = m_current_value | (value & 0x00ff); - if (TRACE_MEM) logerror("tms9980a: memory read low byte %04x -> %02x -> complete word %04x\n", (m_address & m_prgaddr_mask) | 1, value, m_current_value); - break; - } - pulse_clock(1); - m_mem_phase = (m_mem_phase % 4) +1; -} - - -void tms9980a_device::mem_write() -{ - switch (m_mem_phase) - { - case 1: - m_pass = 4; // make the CPU visit this method once more - if (!m_dbin_line.isnull()) m_dbin_line(CLEAR_LINE); - m_prgspace->set_address(m_address & m_prgaddr_mask & ~1); - if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", m_address & m_prgaddr_mask & ~1); - m_prgspace->write_byte(m_address & 0x3ffe & ~1, (m_current_value >> 8)&0xff); - if (TRACE_MEM) logerror("tms9980a: memory write high byte %04x <- %02x\n", m_address & m_prgaddr_mask & ~1, (m_current_value >> 8)&0xff); - m_check_ready = true; - break; - case 2: - // no action here, just wait for READY - break; - case 3: - m_prgspace->set_address((m_address & m_prgaddr_mask) | 1); - if (TRACE_ADDRESSBUS) logerror("tms9980a: set address bus %04x\n", (m_address & m_prgaddr_mask) | 1); - m_prgspace->write_byte((m_address & m_prgaddr_mask) | 1, m_current_value & 0xff); - if (TRACE_MEM) logerror("tms9980a: memory write low byte %04x <- %02x\n", (m_address & m_prgaddr_mask) | 1, m_current_value & 0xff); - break; - case 4: - // no action here, just wait for READY - break; - } - pulse_clock(1); - m_mem_phase = (m_mem_phase % 4) +1; -} - -void tms9980a_device::acquire_instruction() -{ - if (m_mem_phase == 1) - { - if (!m_iaq_line.isnull()) m_iaq_line(ASSERT_LINE); - m_address = PC; - m_first_cycle = m_icount; - } - mem_read(); - - if (m_mem_phase == 1) // changed by mem_read and wrapped - { - decode(m_current_value); - if (TRACE_OP) logerror("tms9980a: ===== Next operation %04x (%s) at %04x =====\n", IR, opname[m_command], PC); - debugger_instruction_hook(this, PC); - PC = (PC + 2) & 0xfffe & m_prgaddr_mask; - } - // IAQ will be cleared in the main loop -} - - - -/**************************************************************************/ -UINT32 tms9980a_device::execute_min_cycles() const -{ - return 2; -} - -// TODO: Compute this value, just a wild guess for the average -UINT32 tms9980a_device::execute_max_cycles() const -{ - return 10; -} - -UINT32 tms9980a_device::execute_input_lines() const -{ - return 8; -} - -// clocks to cycles, cycles to clocks = id -// execute_default_irq_vector = 0 -// execute_burn = nop - -// device_disasm_interface overrides -UINT32 tms9980a_device::disasm_min_opcode_bytes() const -{ - return 2; -} - -UINT32 tms9980a_device::disasm_max_opcode_bytes() const -{ - return 6; -} - -offs_t tms9980a_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) -{ - extern CPU_DISASSEMBLE( tms9980 ); - return CPU_DISASSEMBLE_NAME(tms9980)(this, buffer, pc, oprom, opram, options); -} - -const device_type TMS9980A = &device_creator<tms9980a_device>; diff --git a/src/emu/cpu/tms9900/tms9980a.h b/src/emu/cpu/tms9900/tms9980a.h deleted file mode 100644 index 82492d6073a..00000000000 --- a/src/emu/cpu/tms9900/tms9980a.h +++ /dev/null @@ -1,56 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - TMS9980A. - See tms9980a.c and tms9900.c for documentation -*/ - -#ifndef __TMS9980A_H__ -#define __TMS9980A_H__ - -#include "emu.h" -#include "debugger.h" -#include "tms9900.h" - -enum -{ - INT_9980A_RESET = 0, - INT_9980A_LOAD = 2, - INT_9980A_LEVEL1 = 3, - INT_9980A_LEVEL2 = 4, - INT_9980A_LEVEL3 = 5, - INT_9980A_LEVEL4 = 6, - INT_9980A_CLEAR= 7 -}; - -class tms9980a_device : public tms99xx_device -{ -public: - tms9980a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - -protected: - void mem_read(void); - void mem_write(void); - void acquire_instruction(void); - - void resolve_lines(); - - UINT16 read_workspace_register_debug(int reg); - void write_workspace_register_debug(int reg, UINT16 data); - - UINT32 execute_min_cycles() const; - UINT32 execute_max_cycles() const; - UINT32 execute_input_lines() const; - void execute_set_input(int irqline, int state); - - UINT32 disasm_min_opcode_bytes() const; - UINT32 disasm_max_opcode_bytes() const; - offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options); - address_space_config m_program_config80; - address_space_config m_io_config80; -}; - -// device type definition -extern const device_type TMS9980A; - -#endif /* __TMS9980A_H__ */ diff --git a/src/emu/cpu/tms9900/tms9995.c b/src/emu/cpu/tms9900/tms9995.c deleted file mode 100644 index ae9248fd82d..00000000000 --- a/src/emu/cpu/tms9900/tms9995.c +++ /dev/null @@ -1,3466 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - Texas Instruments TMS9995 - - +----------------+ - XTAL1 | 1 \/ 40| A15,CRUOUT - XTAL2,CLKIN | 2 39| A14 - CLKOUT | 3 38| A13 - D7 | 4 37| A12 - D6 | 5 36| A11 - D5 | 6 35| A10 - D4 | 7 34| A9 - D3 | 8 33| A8 - D2 | 9 32| A7 - Vcc |10 31| Vss - D1 |11 30| A6 - D0 |12 29| A5 - CRUIN |13 28| A4 - /INT4,/EC |14 27| A3 - /INT1 |15 26| A2 - IAQ,HOLDA |16 25| A1 - /DBIN |17 24| A0 - /HOLD |18 23| READY - /WE,/CRUCLK |19 22| /RESET - /MEMEN |20 21| /NMI - +----------------+ - - XTAL1 in Crystal input pin for internal oscillator - XTAL2 in Crystal input pin for internal oscillator, or - CLKIN in Input pin for external oscillator - CLKOUT out Clock output signal (1:4 of the input signal frequency) - CRUIN in CRU input data - /INT4 in Interrupt level 4 input - /EC in Event counter - /INT1 in Interrupt level 1 input - IAQ out Instruction acquisition - HOLDA out Hold acknowledge - /WE out Data available for memory write - /CRUCLK out Communication register unit clock output - /MEMEN out Address bus contains memory address - /NMI in Non-maskable interrupt (/LOAD on TMS9900) - /RESET in Reset interrupt - READY in Memory/External CRU device ready for access - CRUOUT out Communication register unit data output - - Vcc +5V supply - Vss 0V Ground reference - - A0-A15 out Address bus - D0-D7 in/out Data bus - - Note that Texas Instruments' bit numberings define bit 0 as the - most significant bit (different to most other systems). Also, the - system uses big-endian memory organisation: Storing the word 0x1234 at - address 0x0000 means that the byte 0x12 is stored at 0x0000 and byte 0x34 - is stored at 0x0001. - - The TMS9995 is a 16 bit microprocessor like the TMS9900, operating on - 16-bit words and using 16-bit opcodes. Memory transfer of 16-bit words - is achieved by a transfer of the most significant byte, followed by - the least significant byte. - - The 8-bit databus width allows the processor to exchange single bytes with - the external memory. - - See tms9900.c for some more details on the cycle-precise implementation. - - This implementation also features all control lines and the instruction - prefetch mechanism. Prefetching is explicitly triggered within the - microprograms. The TMS9995 specification does not reveal the exact - operations during the microprogram execution, so we have to look at the - required cycle numbers to guess what is happening. - - Auto wait state: - - In order to enable automatic wait state creation, the READY line must be - cleared on reset time. A good position to do this is MACHINE_RESET in - the driver. - - - References (see comments below) - ---------- - [1] Texas Instruments 9900 Microprocessor series: TMS9995 16-bit Microcomputer - - TODO: - - State save - - Test HOLD - - Michael Zapf, June 2012 -*/ - -#include "tms9995.h" - -/* tms9995 ST register bits. */ -enum -{ - ST_LH = 0x8000, // Logical higher (unsigned comparison) - ST_AGT = 0x4000, // Arithmetical greater than (signed comparison) - ST_EQ = 0x2000, // Equal - ST_C = 0x1000, // Carry - ST_OV = 0x0800, // Overflow (when using signed operations) - ST_OP = 0x0400, // Odd parity (used with byte operations) - ST_X = 0x0200, // XOP - ST_OE = 0x0020, // Overflow interrupt enabled - ST_IM = 0x000f // Interrupt mask -}; - -enum -{ - PENDING_NMI = 1, - PENDING_MID = 2, - PENDING_LEVEL1 = 4, - PENDING_OVERFLOW = 8, - PENDING_DECR = 16, - PENDING_LEVEL4 = 32 -}; - -/***************************************************************** - Debugging - Set to 0 (disable) or 1 (enable) -******************************************************************/ - -// Log addresses of executed opcodes -#define TRACE_EXEC 0 - -// Log cycles -#define TRACE_CYCLES 0 - -// Log configuration -#define TRACE_CONFIG 1 - -// Log emulation details -#define TRACE_EMU 0 - -// Log wait/hold states -#define TRACE_WAITHOLD 0 - -// Log microinstruction processing -#define TRACE_MICRO 0 - -// Log interrupts -#define TRACE_INT 0 - -// Log interrupts (detailed phases) -#define TRACE_INTD 0 - -// Log clock pulses -#define TRACE_CLOCK 0 - -// Log READY line input -#define TRACE_READY 0 - -// Log memory access -#define TRACE_MEM 0 - -// Log address bus operation -#define TRACE_ADDRESSBUS 0 - -// Log CRU operations -#define TRACE_CRU 0 - -// Log status register -#define TRACE_STATUS 0 - -// Log operation -#define TRACE_OP 0 - -// Log decrementer operation -#define TRACE_DEC 0 - -// Log with max detail -#define TRACE_DETAIL 0 - -/**************************************************************************** - Constructor -****************************************************************************/ - -tms9995_device::tms9995_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : cpu_device(mconfig, TMS9995, "TMS9995", tag, owner, clock, "tms9995", __FILE__), - m_state_any(0), - PC(0), - PC_debug(0), - m_program_config("program", ENDIANNESS_BIG, 8, 16), - m_io_config("cru", ENDIANNESS_BIG, 8, 16), - m_prgspace(NULL), - m_cru(NULL), - m_external_operation(*this), - m_iaq_line(*this), - m_clock_out_line(*this), - m_holda_line(*this), - m_dbin_line(*this) -{ - m_mp9537 = false; - m_check_overflow = false; -} - -/* - Called from subclass. -*/ -tms9995_device::tms9995_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source) - : cpu_device(mconfig, TMS9995, name, tag, owner, clock, shortname, source), - m_state_any(0), - PC(0), - PC_debug(0), - m_program_config("program", ENDIANNESS_BIG, 8, 16), - m_io_config("cru", ENDIANNESS_BIG, 8, 16), - m_prgspace(NULL), - m_cru(NULL), - m_external_operation(*this), - m_iaq_line(*this), - m_clock_out_line(*this), - m_holda_line(*this), - m_dbin_line(*this) -{ - m_check_overflow = false; -} - - -enum -{ - TMS9995_PC=0, TMS9995_WP, TMS9995_STATUS, TMS9995_IR, - TMS9995_R0, TMS9995_R1, TMS9995_R2, TMS9995_R3, - TMS9995_R4, TMS9995_R5, TMS9995_R6, TMS9995_R7, - TMS9995_R8, TMS9995_R9, TMS9995_R10, TMS9995_R11, - TMS9995_R12, TMS9995_R13, TMS9995_R14, TMS9995_R15 -}; - -void tms9995_device::device_start() -{ - // TODO: Restore save state suport - - m_prgspace = &space(AS_PROGRAM); // dimemory.h - m_cru = &space(AS_IO); - - // Resolve our external connections - m_external_operation.resolve(); - m_iaq_line.resolve(); - m_clock_out_line.resolve(); - m_holda_line.resolve(); - m_dbin_line.resolve(); - - // set our instruction counter - m_icountptr = &m_icount; - - // Clear the interrupt flags - m_int_pending = 0; - - m_mid_flag = false; - m_mid_active = false; - m_nmi_active = false; - m_int_overflow = false; - m_int_decrementer = false; - - m_idle_state = false; - - m_source_value = 0; - - // add the states for the debugger - for (int i=0; i < 20; i++) - { - // callimport = need to use the state_import method to write to the state variable - // callexport = need to use the state_export method to read the state variable - state_add(i, s_statename[i], m_state_any).callimport().callexport().formatstr("%04X"); - } - state_add(STATE_GENPC, "curpc", PC_debug).formatstr("%4s").noshow(); - state_add(STATE_GENFLAGS, "status", m_state_any).callimport().callexport().formatstr("%16s").noshow(); - - // Set up the lookup table for command decoding - build_command_lookup_table(); - - if (TRACE_CONFIG) logerror("%s: Variant = %s, Overflow int = %s\n", tag(), m_mp9537? "MP9537 (no on-chip RAM)" : "with on-chip RAM", m_check_overflow? "check" : "no check"); -} - -void tms9995_device::device_stop() -{ - int k = 0; - if (TRACE_CONFIG) logerror("%s: Deleting lookup tables\n", tag()); - while (m_lotables[k]!=NULL) delete[] m_lotables[k++]; -} - -/* - TMS9995 hard reset - The device reset is just the emulator's trigger for the reset procedure - which is invoked via the main loop. - - This also allows us to check the READY line at reset time, which is used - to enable automatic wait state creation. -*/ -void tms9995_device::device_reset() -{ - m_reset = true; // for the main loop - m_servicing_interrupt = false; // only for debugging - m_request_auto_wait_state = false; - memset(m_flag, 0, sizeof(m_flag)); -} - -const char* tms9995_device::s_statename[20] = -{ - "PC ", "WP ", "ST ", "IR ", - "R0 ", "R1 ", "R2 ", "R3 ", - "R4 ", "R5 ", "R6 ", "R7 ", - "R8 ", "R9 ", "R10", "R11", - "R12", "R13", "R14", "R15" -}; - -/* - Write the contents of a register by external input (debugger) - Note: this is untested any may fail because of the prefetch feature of the CPU. - In particular it may be required to adjust the PC. -*/ -void tms9995_device::state_import(const device_state_entry &entry) -{ - int index = entry.index(); - switch (entry.index()) - { - case STATE_GENFLAGS: - // no action here; we do not allow import, as the flags are all - // bits of the STATUS register - break; - case TMS9995_PC: - PC = (UINT16)m_state_any & 0xfffe; - break; - case TMS9995_WP: - WP = (UINT16)m_state_any & 0xfffe; - break; - case TMS9995_STATUS: - ST = (UINT16)m_state_any; - break; - case TMS9995_IR: - m_instruction->IR = (UINT16)m_state_any; - break; - default: - // Workspace registers - if (index <= TMS9995_R15) - write_workspace_register_debug(index-TMS9995_R0, (UINT16)m_state_any); - break; - } -} - -/* - Reads the contents of a register for display in the debugger. -*/ -void tms9995_device::state_export(const device_state_entry &entry) -{ - int index = entry.index(); - switch (entry.index()) - { - case STATE_GENFLAGS: - m_state_any = ST; - break; - case TMS9995_PC: - m_state_any = PC_debug; - break; - case TMS9995_WP: - m_state_any = WP; - break; - case TMS9995_STATUS: - m_state_any = ST; - break; - case TMS9995_IR: - m_state_any = m_instruction->IR; - break; - default: - // Workspace registers - if (index <= TMS9995_R15) - m_state_any = read_workspace_register_debug(index-TMS9995_R0); - break; - } -} - -/* - state_string_export - export state as a string for the debugger -*/ -void tms9995_device::state_string_export(const device_state_entry &entry, std::string &str) -{ - static const char *statestr = "LAECOPX-----IIII"; - char flags[17]; - memset(flags, 0x00, ARRAY_LENGTH(flags)); - UINT16 val = 0x8000; - if (entry.index()==STATE_GENFLAGS) - { - for (int i=0; i < 16; i++) - { - flags[i] = ((val & ST)!=0)? statestr[i] : '.'; - val = (val >> 1) & 0x7fff; - } - } - str.assign(flags); -} - -/* - Provide access to the workspace registers via the debugger. We have to - take care whether this is in onchip RAM or outside. -*/ -UINT16 tms9995_device::read_workspace_register_debug(int reg) -{ - int temp = m_icount; - UINT16 value; - - int addrb = (WP + (reg << 1)) & 0xfffe; - - if (is_onchip(addrb)) - { - value = (m_onchip_memory[addrb & 0x00fe]<<8) | m_onchip_memory[(addrb & 0x00fe) + 1]; - } - else - { - m_prgspace->set_debugger_access(true); - value = (m_prgspace->read_byte(addrb) << 8) & 0xff00; - value |= m_prgspace->read_byte(addrb+1); - m_prgspace->set_debugger_access(false); - } - m_icount = temp; - return value; -} - -void tms9995_device::write_workspace_register_debug(int reg, UINT16 data) -{ - int temp = m_icount; - int addrb = (WP + (reg << 1)) & 0xfffe; - - if (is_onchip(addrb)) - { - m_onchip_memory[addrb & 0x00fe] = (data >> 8) & 0xff; - m_onchip_memory[(addrb & 0x00fe) + 1] = data & 0xff; - } - else - { - m_prgspace->set_debugger_access(true); - m_prgspace->write_byte(addrb, (data >> 8) & 0xff); - m_prgspace->write_byte(addrb+1, data & 0xff); - m_prgspace->set_debugger_access(false); - } - m_icount = temp; -} - -const address_space_config *tms9995_device::memory_space_config(address_spacenum spacenum) const -{ - switch (spacenum) - { - case AS_PROGRAM: - return &m_program_config; - - case AS_IO: - return &m_io_config; - - default: - return NULL; - } -} - -/************************************************************************** - Microprograms for the CPU instructions - - The actions which are specific to the respective instruction are - invoked by repeated calls of ALU_xxx; each call increases a state - variable so that on the next call, the next part can be processed. - This saves us a lot of additional functions. -**************************************************************************/ - -/* - Define the indices for the micro-operation table. This is done for the sake - of a simpler microprogram definition as an UINT8[]. -*/ -enum -{ - PREFETCH, - PREFETCH_NO_INT, - MEMORY_READ, - MEMORY_WRITE, - WORD_READ, - WORD_WRITE, - OPERAND_ADDR, - INCREG, - INDX, - SET_IMM, - RETADDR, - RETADDR1, - CRU_INPUT, - CRU_OUTPUT, - ABORT, - END, - - ALU_NOP, - ALU_ADD_S_SXC, - ALU_B, - ALU_BLWP, - ALU_C, - ALU_CI, - ALU_CLR_SETO, - ALU_DIV, - ALU_DIVS, - ALU_EXTERNAL, - ALU_F3, - ALU_IMM_ARITHM, - ALU_JUMP, - ALU_LDCR, - ALU_LI, - ALU_LIMIWP, - ALU_LSTWP, - ALU_MOV, - ALU_MPY, - ALU_RTWP, - ALU_SBO_SBZ, - ALU_SHIFT, - ALU_SINGLE_ARITHM, - ALU_STCR, - ALU_STSTWP, - ALU_TB, - ALU_X, - ALU_XOP, - ALU_INT -}; - -#define MICROPROGRAM(_MP) \ - static const UINT8 _MP[] = - -/* - Cycles: - XXXX 1 => needs one cycle - xxxx 1 (1) => needs one cycle when accessing internal memory, two for external mem - PREFETCH 0 (1) => occurs during the last step in parallel, needs one more when fetching from outside - DECODE not shown here; assumed to happen during the next memory cycle; if there is none, - add another cycle - - OPERAND_ADDR x => needs x cycles for address derivation; see the separate table - - Prefetch always needs 1 or 2 cycles; the previous command occurs in parallel - to the prefetch, so we assign a 0 to the previous microprogram step -*/ - -MICROPROGRAM(operand_address_derivation) -{ - RETADDR, 0, 0, 0, // Register direct 0 - WORD_READ, RETADDR, 0, 0, // Register indirect 1 (1) - WORD_READ, RETADDR, 0, 0, // Symbolic 1 (1) - WORD_READ, INCREG, WORD_WRITE, RETADDR1, // Reg indirect auto-increment 3 (1) (1) - WORD_READ, INDX, WORD_READ, RETADDR // Indexed 3 (1) (1) -}; - -MICROPROGRAM(add_s_sxc_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - OPERAND_ADDR, // y - MEMORY_READ, // 1 (1) - ALU_ADD_S_SXC, // 0 (see above, occurs in parallel with PREFETCH) - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) + decode in parallel (0) - END -}; - -MICROPROGRAM(b_mp) -{ - OPERAND_ADDR, // x - ALU_NOP, // 1 Don't read, just use the address - ALU_B, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 Don't save the return address - END -}; - -MICROPROGRAM(bl_mp) -{ - OPERAND_ADDR, // x - ALU_NOP, // 1 Don't read, just use the address - ALU_B, // 0 Re-use the alu operation from B - PREFETCH, // 1 (1) - ALU_NOP, // 1 - MEMORY_WRITE, // 1 (1) Write R11 - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(blwp_mp) -{ - OPERAND_ADDR, // x Determine source address - MEMORY_READ, // 1 (1) - ALU_BLWP, // 1 Got new WP, save it; increase address, save - MEMORY_WRITE, // 1 (1) save old ST to new R15 - ALU_BLWP, // 1 - MEMORY_WRITE, // 1 (1) save old PC to new R14 - ALU_BLWP, // 1 - MEMORY_WRITE, // 1 (1) save old WP to new R13 - ALU_BLWP, // 1 retrieve address - MEMORY_READ, // 1 (1) Read new PC - ALU_BLWP, // 0 Set new PC - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(c_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - OPERAND_ADDR, // y - MEMORY_READ, // 1 (1) - ALU_C, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 decode - END -}; - -MICROPROGRAM(ci_mp) -{ - MEMORY_READ, // 1 (1) (reg) - SET_IMM, // 0 belongs to next cycle - MEMORY_READ, // 1 (1) (imm) - ALU_CI, // 0 set status - PREFETCH, // 1 (1) - ALU_NOP, // 1 decode - END -}; - -MICROPROGRAM(coc_czc_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - ALU_F3, // 0 - MEMORY_READ, // 1 (1) - ALU_F3, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 decode - END -}; - -MICROPROGRAM(clr_seto_mp) -{ - OPERAND_ADDR, // x - ALU_NOP, // 1 - ALU_CLR_SETO, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(divide_mp) // TODO: Verify cycles on the real machine -{ - OPERAND_ADDR, // x Address of divisor S in Q=W1W2/S - MEMORY_READ, // 1 (1) Get S - ALU_DIV, // 1 - MEMORY_READ, // 1 (1) Get W1 - ALU_DIV, // 1 Check for overflow; skip next instruction if not - ABORT, // 1 - MEMORY_READ, // 1 (1) Get W2 - ALU_DIV, // d Calculate quotient - MEMORY_WRITE, // 1 (1) Write quotient to &W1 - ALU_DIV, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) Write remainder to &W2 - END -}; - -MICROPROGRAM(divide_signed_mp) // TODO: Verify cycles on the real machine -{ - OPERAND_ADDR, // x Address of divisor S in Q=W1W2/S - MEMORY_READ, // 1 (1) Get S - ALU_DIVS, // 1 - MEMORY_READ, // 1 (1) Get W1 - ALU_DIVS, // 1 - MEMORY_READ, // 1 (1) Get W2 - ALU_DIVS, // 1 Check for overflow, skip next instruction if not - ABORT, // 1 - ALU_DIVS, // d Calculate quotient - MEMORY_WRITE, // 1 (1) Write quotient to &W1 - ALU_DIVS, // 0 - PREFETCH, // 1 - MEMORY_WRITE, // 1 (1) Write remainder to &W2 - END -}; - -MICROPROGRAM(external_mp) -{ - ALU_NOP, // 1 - ALU_NOP, // 1 - ALU_NOP, // 1 - ALU_NOP, // 1 - ALU_NOP, // 1 - ALU_EXTERNAL, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(imm_arithm_mp) -{ - MEMORY_READ, // 1 (1) - SET_IMM, // 0 - MEMORY_READ, // 1 (1) - ALU_IMM_ARITHM, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(jump_mp) -{ - ALU_NOP, // 1 - ALU_JUMP, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(ldcr_mp) // TODO: Verify cycles -{ - ALU_LDCR, // 1 - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) Get source data - ALU_LDCR, // 1 Save it, point to R12 - WORD_READ, // 1 (1) Get R12 - ALU_LDCR, // 1 Prepare CRU operation - CRU_OUTPUT, // c - ALU_NOP, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(li_mp) -{ - SET_IMM, // 0 - MEMORY_READ, // 1 (1) - ALU_LI, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(limi_lwpi_mp) -{ - SET_IMM, // 0 - MEMORY_READ, // 1 (1) - ALU_NOP, // 1 - ALU_LIMIWP, // 0 lwpi, 1 limi - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(lst_lwp_mp) -{ - MEMORY_READ, // 1 (1) - ALU_NOP, // 1 - ALU_LSTWP, // 0 lwp, 1 lst - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(mov_mp) -{ - OPERAND_ADDR, // 0 - MEMORY_READ, // 1 (1) - OPERAND_ADDR, // 0 - ALU_MOV, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(multiply_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - ALU_MPY, // 1 - MEMORY_READ, // 1 (1) - ALU_MPY, // 17 - MEMORY_WRITE, // 1 (1) - ALU_MPY, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(rtwp_mp) -{ - ALU_RTWP, // 1 - MEMORY_READ, // 1 (1) - ALU_RTWP, // 0 - MEMORY_READ, // 1 (1) - ALU_RTWP, // 0 - MEMORY_READ, // 1 (1) - ALU_RTWP, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(sbo_sbz_mp) -{ - ALU_SBO_SBZ, // 1 Set address = &R12 - WORD_READ, // 1 (1) Read R12 - ALU_SBO_SBZ, // 1 Add offset - CRU_OUTPUT, // 1 output via CRU - PREFETCH, // 1 (1) - ALU_NOP, // 1 - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(shift_mp) -{ - MEMORY_READ, // 1 (1) - ALU_SHIFT, // 2 skip next operation if count != 0 - MEMORY_READ, // 1 (1) if count=0 we must read R0 - ALU_SHIFT, // c do the shift - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(single_arithm_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - ALU_SINGLE_ARITHM, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(stcr_mp) // TODO: Verify on real machine -{ - ALU_STCR, // 1 Check for byte operation - OPERAND_ADDR, // x Source operand - ALU_STCR, // 1 Save, set R12 - WORD_READ, // 1 (1) Read R12 - ALU_STCR, // 1 - CRU_INPUT, // c - ALU_STCR, // 13 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(stst_stwp_mp) -{ - ALU_STSTWP, // 0 - ALU_NOP, // 1 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(tb_mp) -{ - ALU_TB, // 1 - WORD_READ, // 1 (1) - ALU_TB, // 1 - CRU_INPUT, // 2 - ALU_TB, // 0 - PREFETCH, // 1 (1) - ALU_NOP, // 1 - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(x_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - ALU_X, // 1 - END // should not be reached -}; - -MICROPROGRAM(xop_mp) -{ - OPERAND_ADDR, // x Determine source address - ALU_XOP, // 1 Save it; determine XOP number - MEMORY_READ, // 1 (1) Read new WP - ALU_XOP, // 1 - MEMORY_WRITE, // 1 (1) save source address to new R11 - ALU_XOP, // 1 - MEMORY_WRITE, // 1 (1) save old ST to new R15 - ALU_XOP, // 1 - MEMORY_WRITE, // 1 (1) save old PC to new R14 - ALU_XOP, // 1 - MEMORY_WRITE, // 1 (1) save old WP to new R13 - ALU_XOP, // 1 - MEMORY_READ, // 1 (1) Read new PC - ALU_XOP, // 0 set new PC, set X flag - PREFETCH, // 1 (1) - ALU_NOP, // 1 - ALU_NOP, // 1 - END -}; - -MICROPROGRAM(xor_mp) -{ - OPERAND_ADDR, // x - MEMORY_READ, // 1 (1) - ALU_F3, // 0 - MEMORY_READ, // 1 (1) - ALU_F3, // 0 - PREFETCH, // 1 (1) - MEMORY_WRITE, // 1 (1) - END -}; - -MICROPROGRAM(int_mp) -{ - ALU_INT, // 1 - MEMORY_READ, // 1 (1) - ALU_INT, // 2 - MEMORY_WRITE, // 1 (1) - ALU_INT, // 1 - MEMORY_WRITE, // 1 (1) - ALU_INT, // 1 - MEMORY_WRITE, // 1 (1) - ALU_INT, // 1 - MEMORY_READ, // 1 (1) - ALU_INT, // 0 - PREFETCH_NO_INT, // 1 (1) (prefetch happens in parallel to the previous operation) - ALU_NOP, // 1 (+decode in parallel; actually performed right after prefetch) - ALU_NOP, // 1 - END -}; - -const tms9995_device::ophandler tms9995_device::s_microoperation[] = -{ - &tms9995_device::int_prefetch_and_decode, - &tms9995_device::prefetch_and_decode, - &tms9995_device::mem_read, - &tms9995_device::mem_write, - &tms9995_device::word_read, - &tms9995_device::word_write, - &tms9995_device::operand_address_subprogram, - &tms9995_device::increment_register, - &tms9995_device::indexed_addressing, - &tms9995_device::set_immediate, - &tms9995_device::return_with_address, - &tms9995_device::return_with_address_copy, - &tms9995_device::cru_input_operation, - &tms9995_device::cru_output_operation, - &tms9995_device::abort_operation, - &tms9995_device::command_completed, - - &tms9995_device::alu_nop, - &tms9995_device::alu_add_s_sxc, - &tms9995_device::alu_b, - &tms9995_device::alu_blwp, - &tms9995_device::alu_c, - &tms9995_device::alu_ci, - &tms9995_device::alu_clr_seto, - &tms9995_device::alu_divide, - &tms9995_device::alu_divide_signed, - &tms9995_device::alu_external, - &tms9995_device::alu_f3, - &tms9995_device::alu_imm_arithm, - &tms9995_device::alu_jump, - &tms9995_device::alu_ldcr, - &tms9995_device::alu_li, - &tms9995_device::alu_limi_lwpi, - &tms9995_device::alu_lst_lwp, - &tms9995_device::alu_mov, - &tms9995_device::alu_multiply, - &tms9995_device::alu_rtwp, - &tms9995_device::alu_sbo_sbz, - &tms9995_device::alu_shift, - &tms9995_device::alu_single_arithm, - &tms9995_device::alu_stcr, - &tms9995_device::alu_stst_stwp, - &tms9995_device::alu_tb, - &tms9995_device::alu_x, - &tms9995_device::alu_xop, - &tms9995_device::alu_int -}; - -/***************************************************************************** - CPU instructions -*****************************************************************************/ - -/* - Available instructions - MID is not a real instruction but stands for an invalid operation which - triggers a "macro instruction detect" interrupt. Neither is INTR which - indicates an interrupt handling in progress. -*/ -enum -{ - MID=0, A, AB, ABS, AI, ANDI, B, BL, BLWP, C, - CB, CI, CKOF, CKON, CLR, COC, CZC, DEC, DECT, DIV, - DIVS, IDLE, INC, INCT, INV, JEQ, JGT, JH, JHE, JL, - JLE, JLT, JMP, JNC, JNE, JNO, JOC, JOP, LDCR, LI, - LIMI, LREX, LST, LWP, LWPI, MOV, MOVB, MPY, MPYS, NEG, - ORI, RSET, RTWP, S, SB, SBO, SBZ, SETO, SLA, SOC, - SOCB, SRA, SRC, SRL, STCR, STST, STWP, SWPB, SZC, SZCB, - TB, X, XOP, XOR, INTR -}; - -static const char opname[][5] = -{ "MID ", "A ", "AB ", "ABS ", "AI ", "ANDI", "B ", "BL ", "BLWP", "C ", - "CB ", "CI ", "CKOF", "CKON", "CLR ", "COC ", "CZC ", "DEC ", "DECT", "DIV ", - "DIVS", "IDLE", "INC ", "INCT", "INV ", "JEQ ", "JGT ", "JH ", "JHE ", "JL ", - "JLE ", "JLT ", "JMP ", "JNC ", "JNE ", "JNO ", "JOC ", "JOP ", "LDCR", "LI ", - "LIMI", "LREX", "LST ", "LWP ", "LWPI", "MOV ", "MOVB", "MPY ", "MPYS", "NEG ", - "ORI ", "RSET", "RTWP", "S ", "SB ", "SBO ", "SBZ ", "SETO", "SLA ", "SOC ", - "SOCB", "SRA ", "SRC ", "SRL ", "STCR", "STST", "STWP", "SWPB", "SZC ", "SZCB", - "TB ", "X ", "XOP ", "XOR ", "*int" -}; - -/* - Formats: - - 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 - ----+------------------------------------------------+ - 1 | Opcode | B | Td | RegNr | Ts | RegNr | - +--------+---+----+------------+----+------------+ - 2 | Opcode | Displacement | - +-----------------------+------------------------+ - 3 | Opcode | RegNr | Ts | RegNr | - +-----------------+------------+----+------------+ - 4 | Opcode | Count | Ts | RegNr | - +-----------------+------------+----+------------+ - 5 | Opcode | Count | RegNr | - +-----------------------+-----------+------------+ - 6 | Opcode | Ts | RegNr | - +------------------------------+----+------------+ - 7 | Opcode |0| 0| 0| 0| 0 | - +---------------------------------+-+--+--+--+---+ - 8 | Opcode |0| RegNr | - +---------------------------------+-+------------+ - 9 | Opcode | Reg/Nr | Ts | RegNr | - +-----------------+------------+----+------------+ - 10 | Opcode | Ts | RegNr | (DIVS, MPYS) - +------------------------------+----+------------+ - 11 | Opcode | RegNr | (LST, LWP) - +-----------------------------------+------------+ -*/ - -/* - Defines the number of bits from the left which are significant for the - command in the respective format. -*/ -static const int format_mask_len[] = -{ - 0, 4, 8, 6, 6, 8, 10, 16, 12, 6, 10, 12 -}; - -const tms9995_device::tms_instruction tms9995_device::s_command[] = -{ - // Base opcode list - // Opcode, ID, format, microprg - { 0x0080, LST, 11, lst_lwp_mp }, - { 0x0090, LWP, 11, lst_lwp_mp }, - { 0x0180, DIVS, 10, divide_signed_mp }, - { 0x01C0, MPYS, 10, multiply_mp }, - { 0x0200, LI, 8, li_mp }, - { 0x0220, AI, 8, imm_arithm_mp }, - { 0x0240, ANDI, 8, imm_arithm_mp }, - { 0x0260, ORI, 8, imm_arithm_mp }, - { 0x0280, CI, 8, ci_mp }, - { 0x02a0, STWP, 8, stst_stwp_mp }, - { 0x02c0, STST, 8, stst_stwp_mp }, - { 0x02e0, LWPI, 8, limi_lwpi_mp }, - { 0x0300, LIMI, 8, limi_lwpi_mp }, - { 0x0340, IDLE, 7, external_mp }, - { 0x0360, RSET, 7, external_mp }, - { 0x0380, RTWP, 7, rtwp_mp }, - { 0x03a0, CKON, 7, external_mp }, - { 0x03c0, CKOF, 7, external_mp }, - { 0x03e0, LREX, 7, external_mp }, - { 0x0400, BLWP, 6, blwp_mp }, - { 0x0440, B, 6, b_mp }, - { 0x0480, X, 6, x_mp }, - { 0x04c0, CLR, 6, clr_seto_mp }, - { 0x0500, NEG, 6, single_arithm_mp }, - { 0x0540, INV, 6, single_arithm_mp }, - { 0x0580, INC, 6, single_arithm_mp }, - { 0x05c0, INCT, 6, single_arithm_mp }, - { 0x0600, DEC, 6, single_arithm_mp }, - { 0x0640, DECT, 6, single_arithm_mp }, - { 0x0680, BL, 6, bl_mp }, - { 0x06c0, SWPB, 6, single_arithm_mp }, - { 0x0700, SETO, 6, clr_seto_mp }, - { 0x0740, ABS, 6, single_arithm_mp }, - { 0x0800, SRA, 5, shift_mp }, - { 0x0900, SRL, 5, shift_mp }, - { 0x0a00, SLA, 5, shift_mp }, - { 0x0b00, SRC, 5, shift_mp }, - { 0x1000, JMP, 2, jump_mp }, - { 0x1100, JLT, 2, jump_mp }, - { 0x1200, JLE, 2, jump_mp }, - { 0x1300, JEQ, 2, jump_mp }, - { 0x1400, JHE, 2, jump_mp }, - { 0x1500, JGT, 2, jump_mp }, - { 0x1600, JNE, 2, jump_mp }, - { 0x1700, JNC, 2, jump_mp }, - { 0x1800, JOC, 2, jump_mp }, - { 0x1900, JNO, 2, jump_mp }, - { 0x1a00, JL, 2, jump_mp }, - { 0x1b00, JH, 2, jump_mp }, - { 0x1c00, JOP, 2, jump_mp }, - { 0x1d00, SBO, 2, sbo_sbz_mp }, - { 0x1e00, SBZ, 2, sbo_sbz_mp }, - { 0x1f00, TB, 2, tb_mp }, - { 0x2000, COC, 3, coc_czc_mp }, - { 0x2400, CZC, 3, coc_czc_mp }, - { 0x2800, XOR, 3, xor_mp }, - { 0x2c00, XOP, 3, xop_mp }, - { 0x3000, LDCR, 4, ldcr_mp }, - { 0x3400, STCR, 4, stcr_mp }, - { 0x3800, MPY, 9, multiply_mp }, - { 0x3c00, DIV, 9, divide_mp }, - { 0x4000, SZC, 1, add_s_sxc_mp }, - { 0x5000, SZCB, 1, add_s_sxc_mp }, - { 0x6000, S, 1, add_s_sxc_mp }, - { 0x7000, SB, 1, add_s_sxc_mp }, - { 0x8000, C, 1, c_mp }, - { 0x9000, CB, 1, c_mp }, - { 0xa000, A, 1, add_s_sxc_mp }, - { 0xb000, AB, 1, add_s_sxc_mp }, - { 0xc000, MOV, 1, mov_mp }, - { 0xd000, MOVB, 1, mov_mp }, - { 0xe000, SOC, 1, add_s_sxc_mp }, - { 0xf000, SOCB, 1, add_s_sxc_mp } -}; - -/* - Create a B-tree for looking up the commands. Each node can carry up to - 16 entries, indexed by 4 consecutive bits in the opcode. - - See tms9900.c for a detailed description. -*/ -void tms9995_device::build_command_lookup_table() -{ - int i = 0; - int cmdindex = 0; - int bitcount; - const tms_instruction *inst; - UINT16 opcode; - int k = 0; - - m_command_lookup_table = new lookup_entry[16]; - // We use lotables as a list of allocated tables - to be able to delete them - // at the end. - m_lotables[k++] = m_command_lookup_table; - - lookup_entry* table = m_command_lookup_table; - for (int j=0; j < 16; j++) - { - table[j].entry = NULL; - table[j].next_digit = NULL; - } - - do - { - inst = &s_command[i]; - table = m_command_lookup_table; - if (TRACE_EMU) logerror("tms9995: === opcode=%04x, len=%d\n", inst->opcode, format_mask_len[inst->format]); - bitcount = 4; - opcode = inst->opcode; - cmdindex = (opcode>>12) & 0x000f; - - while (bitcount < format_mask_len[inst->format]) - { - // Descend - if (table[cmdindex].next_digit == NULL) - { - if (TRACE_EMU) logerror("tms9995: create new table at bitcount=%d for index=%d\n", bitcount, cmdindex); - table[cmdindex].next_digit = new lookup_entry[16]; - m_lotables[k++] = table[cmdindex].next_digit; - for (int j=0; j < 16; j++) - { - table[cmdindex].next_digit[j].next_digit = NULL; - table[cmdindex].next_digit[j].entry = NULL; - } - } - else - { - if (TRACE_EMU) logerror("tms9995: found a table at bitcount=%d\n", bitcount); - } - - table = table[cmdindex].next_digit; - - bitcount = bitcount+4; - opcode <<= 4; - cmdindex = (opcode>>12) & 0x000f; - if (TRACE_EMU) logerror("tms9995: next index=%x\n", cmdindex); - } - - if (TRACE_EMU) logerror("tms9995: bitcount=%d\n", bitcount); - // We are at the target level - // Need to fill in the same entry for all values in the bitcount - // (if a command needs 10 bits we have to copy it four - // times for all combinations with 12 bits) - for (int j=0; j < (1<<(bitcount-format_mask_len[inst->format])); j++) - { - if (TRACE_EMU) logerror("tms9995: opcode=%04x at position %d\n", inst->opcode, cmdindex+j); - table[cmdindex+j].entry = inst; - } - - i++; - } while (inst->opcode != 0xf000); - - m_lotables[k++] = NULL; - if (TRACE_EMU) logerror("tms9995: Allocated %d tables\n", k); -} - -/* - Main execution loop - - For each invocation of execute_run, a number of loop iterations has been - calculated before (m_icount). Each loop iteration is one clock cycle. - The loop must be executed for the number of times that corresponds to the - time until the next timer event. -*/ -void tms9995_device::execute_run() -{ - if (m_reset) service_interrupt(); - - if (TRACE_EMU) logerror("tms9995: calling execute_run for %d cycles\n", m_icount); - do - { - // Normal operation - if (m_check_ready && m_ready == false) - { - // We are in a wait state - if (TRACE_WAITHOLD) logerror("tms9995: wait state\n"); - // The clock output should be used to change the state of an outer - // device which operates the READY line - pulse_clock(1); - } - else - { - if (m_check_hold && m_hold_state) - { - set_hold_state(true); - if (TRACE_WAITHOLD) logerror("tms9995: hold state\n"); - pulse_clock(1); - } - else - { - set_hold_state(false); - - m_check_ready = false; - - if (TRACE_MICRO) logerror("tms9995: main loop, operation %s, MPC = %d\n", opname[m_instruction->command], MPC); - (this->*s_microoperation[m_instruction->program[MPC]])(); - - // For multi-pass operations where the MPC should not advance - // or when we have put in a new microprogram - m_pass--; - if (m_pass<=0) - { - m_pass = 1; - MPC++; - } - } - } - } while (m_icount>0 && !m_reset); - if (TRACE_EMU) logerror("tms9995: cycles expired; will return soon.\n"); -} - -/**************************************************************************/ - -/* - Interrupt input - output - m_nmi_state - m_irq_level - flag[2], flag[4] -*/ -void tms9995_device::execute_set_input(int irqline, int state) -{ - if (irqline==INT_9995_RESET && state==ASSERT_LINE) - { - m_reset = true; - } - else - { - if (irqline == INPUT_LINE_NMI) - { - m_nmi_active = (state==ASSERT_LINE); - if (TRACE_INT) logerror("tms9995: NMI interrupt line state=%d\n", state); - } - else - { - if (irqline == INT_9995_INT1) - { - m_int1_active = m_flag[2] = (state==ASSERT_LINE); - if (TRACE_INT) logerror("tms9995: Line INT1 state=%d\n", state); - } - else - { - if (irqline == INT_9995_INT4) - { - if (TRACE_INT) logerror("tms9995: Line INT4/EC state=%d\n", state); - if (m_flag[0]==false) - { - if (TRACE_INT) logerror("tms9995: set as interrupt\n"); - m_int4_active = m_flag[4] = (state==ASSERT_LINE); - } - else - { - if (TRACE_INT) logerror("tms9995: set as event count\n"); - trigger_decrementer(); - } - } - else - { - logerror("tms9995: Accessed invalid interrupt line %d\n", irqline); - } - } - } - } -} - -/* - Issue a pulse on the clock line. -*/ -void tms9995_device::pulse_clock(int count) -{ - for (int i=0; i < count; i++) - { - if (!m_clock_out_line.isnull()) m_clock_out_line(ASSERT_LINE); - m_ready = m_ready_bufd && !m_request_auto_wait_state; // get the latched READY state - if (!m_clock_out_line.isnull()) m_clock_out_line(CLEAR_LINE); - m_icount--; // This is the only location where we count down the cycles. - if (TRACE_CLOCK) - { - if (m_check_ready) logerror("tms9995: pulse_clock, READY=%d, auto_wait=%d\n", m_ready_bufd? 1:0, m_auto_wait? 1:0); - else logerror("tms9995: pulse_clock\n"); - } - m_request_auto_wait_state = false; - if (m_flag[0] == false && m_flag[1] == true) - { - // Section 2.3.1.2.2: "by decreasing the count in the Decrementing - // Register by one for each fourth CLKOUT cycle" - m_decrementer_clkdiv = (m_decrementer_clkdiv+1)%4; - if (m_decrementer_clkdiv==0) trigger_decrementer(); - } - } -} - -/* - Enter the hold state. -*/ -void tms9995_device::set_hold(int state) -{ - m_hold_state = (state==ASSERT_LINE); - if (TRACE_WAITHOLD) logerror("tms9995: set HOLD = %d\n", state); - if (!m_hold_state) - { - if (!m_holda_line.isnull()) m_holda_line(CLEAR_LINE); - } -} - -/* - Signal READY to the CPU. When cleared, the CPU enters wait states. This - becomes effective on a clock pulse. -*/ -void tms9995_device::set_ready(int state) -{ - m_ready_bufd = (state==ASSERT_LINE); - if (TRACE_READY) logerror("tms9995: set READY = %d\n", m_ready_bufd? 1 : 0); -} - -/* - When the divide operations fail, we get to this operation. -*/ -void tms9995_device::abort_operation() -{ - int_prefetch_and_decode(); // do not forget to prefetch - // And don't forget that prefetch is a 2-pass operation, so this method - // will be called a second time. Only when the lowbyte has been fetched, - // continue with the next step - if (m_mem_phase==1) command_completed(); -} - -/* - Enter or leave the hold state. We only operate the HOLDA line when there is a change. -*/ -inline void tms9995_device::set_hold_state(bool state) -{ - if (m_hold_state != state) - if (!m_holda_line.isnull()) m_holda_line(state? ASSERT_LINE : CLEAR_LINE); - m_hold_state = state; -} - -/* - Decode the instruction. This is done in parallel to other operations - so we just do it together with the prefetch. -*/ -void tms9995_device::decode(UINT16 inst) -{ - int index = 0; - lookup_entry* table = m_command_lookup_table; - UINT16 opcode = inst; - bool complete = false; - const tms_instruction *decoded; - - int dindex = (m_instindex==0)? 1:0; - - m_mid_active = false; - - while (!complete) - { - index = (opcode >> 12) & 0x000f; - if (TRACE_EMU) logerror("tms9995: Check next hex digit of instruction %x\n", index); - if (table[index].next_digit != NULL) - { - table = table[index].next_digit; - opcode = opcode << 4; - } - else complete = true; - } - decoded = table[index].entry; - if (decoded == NULL) - { - // not found - logerror("tms9995: Undefined opcode %04x at logical address %04x, will trigger MID\n", inst, PC); - m_decoded[dindex].IR = 0; - m_decoded[dindex].command = MID; - } - else - { - m_decoded[dindex].IR = inst; - m_decoded[dindex].command = decoded->id; - m_decoded[dindex].program = decoded->prog; - m_decoded[dindex].byteop = ((decoded->format == 1) && ((inst & 0x1000)!=0)); - m_decoded[dindex].state = 0; - if (TRACE_EMU) logerror("tms9995: Command decoded as id %d, %s, base opcode %04x\n", decoded->id, opname[decoded->id], decoded->opcode); - m_pass = 1; - } -} - -/* - Fetch the next instruction and check pending interrupts before. - Getting an instruction is a normal memory access (plus an asserted IAQ line), - so this is subject to wait state handling. We have to allow for a two-pass - handling. -*/ -void tms9995_device::int_prefetch_and_decode() -{ - bool check_int = (m_instruction->command != XOP && m_instruction->command != BLWP); - int intmask = ST & 0x000f; - - if (m_mem_phase == 1) - { - // Check interrupt lines - if (m_nmi_active) - { - if (TRACE_INT) logerror("tms9995: Checking interrupts ... NMI active\n"); - m_int_pending |= PENDING_NMI; - m_idle_state = false; - PC = (PC + 2) & 0xfffe; // we have not prefetched the next instruction - return; - } - else - { - m_int_pending = 0; - - if (check_int) - { - if (m_int1_active && intmask >= 1) m_int_pending |= PENDING_LEVEL1; - if (m_int_overflow && intmask >= 2) m_int_pending |= PENDING_OVERFLOW; - if (m_int_decrementer && intmask >= 3) m_int_pending |= PENDING_DECR; - if (m_int4_active && intmask >= 4) m_int_pending |= PENDING_LEVEL4; - } - - if (m_int_pending!=0) - { - if (m_idle_state) - { - m_idle_state = false; - if (TRACE_INT) logerror("tms9995: Interrupt occurred, terminate IDLE state\n"); - } - PC = PC + 2; // PC must be advanced (see flow chart), but no prefetch - if (TRACE_INT) logerror("tms9995: Interrupts pending; no prefetch; advance PC to %04x\n", PC); - return; - } - else - { - if (TRACE_INT) logerror("tms9995: Checking interrupts ... none pending\n"); - // No pending interrupts - if (m_idle_state) - { - if (TRACE_WAITHOLD) logerror("tms9995: IDLE state\n"); - // We are IDLE, stay in the loop and do not advance the PC - m_pass = 2; - pulse_clock(1); - return; - } - } - } - } - - // We reach this point in phase 1 if there is no interrupt and in all other phases - prefetch_and_decode(); -} - -/* - The actual prefetch operation, but without the interrupt check. This one is - needed when we complete the interrupt handling and need to get the next - instruction. According to the flow chart in [1], the prefetch after the - interrupt handling ignores other pending interrupts. -*/ -void tms9995_device::prefetch_and_decode() -{ - if (m_mem_phase==1) - { - // Fetch next instruction - // Save these values; they have been computed during the current instruction execution - m_address_copy = m_address; - m_value_copy = m_current_value; - if (!m_iaq_line.isnull()) m_iaq_line(ASSERT_LINE); - m_address = PC; - if (TRACE_OP) logerror("tms9995: **** Prefetching new instruction at %04x ****\n", PC); - } - - word_read(); // changes m_mem_phase - - if (m_mem_phase==1) - { - // We're back in phase 1, i.e. the whole prefetch is done - decode(m_current_value); // This is for free; in reality it is in parallel with the next memory operation - m_address = m_address_copy; // restore m_address - m_current_value = m_value_copy; // restore m_current_value - PC = (PC + 2) & 0xfffe; // advance PC - if (!m_iaq_line.isnull()) m_iaq_line(CLEAR_LINE); - if (TRACE_OP) logerror("tms9995: ++++ Prefetch done ++++\n"); - } -} - -/* - Used by the normal command completion as well as by the X operation. We - assume that we have a fully decoded operation which was previously - prefetched. -*/ -void tms9995_device::next_command() -{ - int next = (m_instindex==0)? 1:0; - - if (m_decoded[next].command == MID) - { - m_mid_flag = true; - m_mid_active = true; - service_interrupt(); - } - else - { - m_instindex = next; - m_instruction = &m_decoded[m_instindex]; - m_get_destination = false; - // This is a preset for opcodes which do not need an opcode address derivation - m_address = WP + ((m_instruction->IR & 0x000f)<<1); - MPC = -1; - if (TRACE_OP) logerror("tms9995: ===== Next operation %04x (%s) at %04x =====\n", m_instruction->IR, opname[m_instruction->command], PC-2); - - if (TRACE_EXEC) - { - if (m_servicing_interrupt) logerror("i%04x\n", PC-2); - else logerror("%04x\n", PC-2); - } - PC_debug = PC - 2; - debugger_instruction_hook(this, PC_debug); - m_first_cycle = m_icount; - } -} - -/* - End of command execution -*/ -void tms9995_device::command_completed() -{ - // Pseudo state at the end of the current instruction cycle sequence - if (TRACE_CYCLES) - { - logerror("tms9995: +++++ Instruction %04x (%s) completed", m_instruction->IR, opname[m_instruction->command]); - int cycles = m_first_cycle - m_icount; - // Avoid nonsense values due to expired and resumed main loop - if (cycles > 0 && cycles < 10000) logerror(", consumed %d cycles", cycles); - logerror(" +++++\n"); - } - - if (m_int_pending != 0) - { - service_interrupt(); - } - else - { - if ((ST & ST_OE)!=0 && (ST & ST_OV)!=0 && (ST & 0x000f)>2) - { - service_interrupt(); - } - else - { - next_command(); - } - } -} - -/* - Handle pending interrupts. -*/ -void tms9995_device::service_interrupt() -{ - int vectorpos = 0; - - if (m_reset) - { - vectorpos = 0; - m_intmask = 0; // clear interrupt mask - - m_nmi_state = false; - m_hold_state = false; - m_mem_phase = 1; - m_check_hold = false; - m_word_access = false; - m_int1_active = false; - m_int4_active = false; - m_decrementer_clkdiv = 0; - - m_pass = 0; - m_instindex = 0; - m_instruction = &m_decoded[m_instindex]; - - memset(m_flag, 0, sizeof(m_flag)); - - ST = 0; - - // The auto-wait state generation is turned on when the READY line is cleared - // on RESET. - m_auto_wait = !m_ready_bufd; - if (TRACE_CONFIG) logerror("tms9995: RESET; automatic wait state creation is %s\n", m_auto_wait? "enabled":"disabled"); - // We reset the READY flag, or the CPU will not start - m_ready_bufd = true; - } - else - { - if (m_mid_active) - { - vectorpos = 0x0008; - m_intmask = 0x0001; - PC = (PC + 2) & 0xfffe; - if (TRACE_INT) logerror("tms9995: ***** MID pending\n"); - m_mid_active = false; - } - else - { - if ((m_int_pending & PENDING_NMI)!=0) - { - vectorpos = 0xfffc; - m_int_pending &= ~PENDING_NMI; - m_intmask = 0; - if (TRACE_INT) logerror("tms9995: ***** NMI pending\n"); - } - else - { - if ((m_int_pending & PENDING_LEVEL1)!=0) - { - vectorpos = 0x0004; - m_int_pending &= ~PENDING_LEVEL1; - m_flag[2] = false; - m_intmask = 0; - if (TRACE_INT) logerror("tms9995: ***** INT1 pending\n"); - } - else - { - if ((m_int_pending & PENDING_OVERFLOW)!=0) - { - vectorpos = 0x0008; - m_int_pending &= ~PENDING_OVERFLOW; - m_intmask = 0x0001; - if (TRACE_INT) logerror("tms9995: ***** OVERFL pending\n"); - } - else - { - if ((m_int_pending & PENDING_DECR)!=0) - { - vectorpos = 0x000c; - m_intmask = 0x0002; - m_int_pending &= ~PENDING_DECR; - m_flag[3] = false; - m_int_decrementer = false; - if (TRACE_DEC) logerror("tms9995: ***** DECR pending\n"); - } - else - { - vectorpos = 0x0010; - m_intmask = 0x0003; - m_int_pending &= ~PENDING_LEVEL4; - m_flag[4] = false; - if (TRACE_INT) logerror("tms9995: ***** INT4 pending\n"); - } - } - } - } - } - } - - if (TRACE_INT) logerror("tms9995: ********* triggered an interrupt with vector %04x/%04x\n", vectorpos, vectorpos+2); - - // just for debugging purposes - m_servicing_interrupt = true; - - // The microinstructions will do the context switch - m_address = vectorpos; - m_instruction->program = int_mp; - m_instruction->state = 0; - m_instruction->byteop = false; - m_instruction->command = INTR; - m_pass = m_reset? 1 : 2; - m_from_reset = m_reset; - - if (m_reset) - { - m_instruction->IR = 0x0000; - m_reset = false; - } - MPC = 0; - m_first_cycle = m_icount; - m_check_ready = false; // set to default -} - -/* - Read memory. This method expects as input m_address, and delivers the value - in m_current_value. For a single byte read, the byte is put into the high byte. - This method uses the m_pass variable to achieve a two-pass handling for - getting the complete word (high byte, low byte). - - input: - m_address - m_lowbyte - output: - m_current_value - - m_address is unchanged - - Make sure that m_lowbyte is false on the first call. -*/ -void tms9995_device::mem_read() -{ - // First determine whether the memory is inside the CPU - // On-chip memory is F000 ... F0F9, F0FA-FFF9 = off-chip, FFFA/B = Decrementer - // FFFC-FFFF = NMI vector (on-chip) - // There is a variant of the TMS9995 with no on-chip RAM which was used - // for the TI-99/8 (9537). - - if ((m_address & 0xfffe)==0xfffa && !m_mp9537) - { - if (TRACE_DEC) logerror("tms9995: read decrementer\n"); - // Decrementer mapped into the address space - m_current_value = m_decrementer_value; - if (m_instruction->byteop) - { - if ((m_address & 1)!=1) m_current_value <<= 8; - m_current_value &= 0xff00; - } - pulse_clock(1); - return; - } - - if (is_onchip(m_address)) - { - // If we have a word access, we have to align the address - // This is the case for word operations and for certain phases of - // byte operations (e.g. when retrieving the index register) - if (m_word_access || !m_instruction->byteop) m_address &= 0xfffe; - - if (TRACE_MEM) logerror("tms9995: read onchip memory (single pass, address %04x)\n", m_address); - - // Ignore the READY state - m_check_ready = false; - // We put fffc-ffff back into the f000-f0ff area - m_current_value = m_onchip_memory[m_address & 0x00ff]<<8; - if (m_word_access || !m_instruction->byteop) - { - // We have a word operation; add the low byte right here (just 1 cycle) - m_current_value |= (m_onchip_memory[(m_address & 0x00ff)+1] & 0xff); - } - pulse_clock(1); - } - else - { - // This is an off-chip access - m_check_ready = true; - UINT8 value; - UINT16 address = m_address; - - switch (m_mem_phase) - { - case 1: - // Set address - // If this is a word access, 4 passes, else 2 passes - if (!m_dbin_line.isnull()) m_dbin_line(ASSERT_LINE); - if (m_word_access || !m_instruction->byteop) - { - m_pass = 4; - // For word accesses, we always start at the even address - address &= 0xfffe; - } - else m_pass = 2; - - m_check_hold = false; - if (TRACE_ADDRESSBUS) logerror("tms9995: set address bus %04x\n", m_address & ~1); - m_prgspace->set_address(address); - m_request_auto_wait_state = m_auto_wait; - pulse_clock(1); - break; - case 2: - // Sample the value on the data bus (high byte) - if (m_word_access || !m_instruction->byteop) address &= 0xfffe; - value = m_prgspace->read_byte(address); - if (TRACE_MEM) logerror("tms9995: memory read byte %04x -> %02x\n", m_address & ~1, value); - m_current_value = (value << 8) & 0xff00; - break; - case 3: - // Set address + 1 (unless byte command) - if (TRACE_ADDRESSBUS) logerror("tms9995: set address bus %04x\n", m_address | 1); - m_prgspace->set_address(m_address | 1); - pulse_clock(1); - break; - case 4: - // Read low byte - value = m_prgspace->read_byte(m_address | 1); - m_current_value |= value; - if (TRACE_MEM) logerror("tms9995: memory read byte %04x -> %02x, complete word = %04x\n", m_address | 1, value, m_current_value); - m_check_hold = true; - break; - } - - m_mem_phase = (m_mem_phase % 4) +1; - - // Reset to 1 when we are done - if (m_pass==1) m_mem_phase = 1; - } -} - -/* - Read a word. This is independent of the byte flag of the instruction. - We need this variant especially when we have to retrieve a register value - in indexed addressing within a byte-oriented operation. -*/ -inline void tms9995_device::word_read() -{ - m_word_access = true; - mem_read(); - m_word_access = false; -} - -/* - Write memory. This method expects as input m_address and m_current_value. - For a single byte write, the byte to be written is expected to be in the - high byte of m_current_value. - This method uses the m_pass variable to achieve a two-pass handling for - writing the complete word (high byte, low byte). - - input: - m_address - m_lowbyte - m_current_value - - output: - - - m_address is unchanged - - Make sure that m_lowbyte is false on the first call. -*/ -void tms9995_device::mem_write() -{ - if ((m_address & 0xfffe)==0xfffa && !m_mp9537) - { - if (m_instruction->byteop) - { - // According to [1], section 2.3.1.2.2: - // "The decrementer should always be accessed as a full word. [...] - // Writing a single byte to either of the bytes of the decrementer - // will result in the data byte being written into the byte specifically addressed - // and random bits being written into the other byte of the decrementer." - - // So we just don't care about the low byte. - if (m_address == 0xfffb) m_current_value >>= 8; - - // dito: "This also loads the Decrementing Register with the same count." - m_starting_count_storage_register = m_decrementer_value = m_current_value; - } - else - { - m_starting_count_storage_register = m_decrementer_value = m_current_value; - } - if (TRACE_DEC) logerror("tms9995: Setting decrementer to %04x, PC=%04x\n", m_current_value, PC); - pulse_clock(1); - return; - } - - if (is_onchip(m_address)) - { - // If we have a word access, we have to align the address - // This is the case for word operations and for certain phases of - // byte operations (e.g. when retrieving the index register) - if (m_word_access || !m_instruction->byteop) m_address &= 0xfffe; - - if (TRACE_MEM) logerror("tms9995: write to onchip memory (single pass, address %04x, value=%04x)\n", m_address, m_current_value); - m_check_ready = false; - m_onchip_memory[m_address & 0x00ff] = (m_current_value >> 8) & 0xff; - if (m_word_access || !m_instruction->byteop) - { - m_onchip_memory[(m_address & 0x00ff)+1] = m_current_value & 0xff; - } - pulse_clock(1); - } - else - { - // This is an off-chip access - m_check_ready = true; - UINT16 address = m_address; - switch (m_mem_phase) - { - case 1: - // Set address - // If this is a word access, 4 passes, else 2 passes - if (!m_dbin_line.isnull()) m_dbin_line(CLEAR_LINE); - - if (m_word_access || !m_instruction->byteop) - { - m_pass = 4; - address &= 0xfffe; - } - else m_pass = 2; - - m_check_hold = false; - if (TRACE_ADDRESSBUS) logerror("tms9995: set address bus %04x\n", address); - m_prgspace->set_address(address); - if (TRACE_MEM) logerror("tms9995: memory write byte %04x <- %02x\n", address, (m_current_value >> 8)&0xff); - m_prgspace->write_byte(address, (m_current_value >> 8)&0xff); - pulse_clock(1); - break; - - case 2: - // no action here, just wait for READY - break; - case 3: - // Set address + 1 (unless byte command) - if (TRACE_ADDRESSBUS) logerror("tms9995: set address bus %04x\n", m_address | 1); - m_prgspace->set_address(m_address | 1); - if (TRACE_MEM) logerror("tms9995: memory write byte %04x <- %02x\n", m_address | 1, m_current_value & 0xff); - m_prgspace->write_byte(m_address | 1, m_current_value & 0xff); - pulse_clock(1); - break; - case 4: - // no action here, just wait for READY - m_check_hold = true; - break; - } - - m_mem_phase = (m_mem_phase % 4) +1; - - // Reset to 1 when we are done - if (m_pass==1) m_mem_phase = 1; - } -} - -/* - Write a word. This is independent of the byte flag of the instruction. -*/ -inline void tms9995_device::word_write() -{ - m_word_access = true; - mem_write(); - m_word_access = false; -} - -/* - Returns from the operand address derivation. -*/ -void tms9995_device::return_with_address() -{ - // Return from operand address derivation - // The result should be in m_address - m_instruction->program = m_caller; - MPC = m_caller_MPC; // will be increased on return - m_address = m_current_value + m_address_add; - if (TRACE_DETAIL) logerror("tms9995: +++ return from operand address derivation +++\n"); - // no clock pulse -} - -/* - Returns from the operand address derivation, but using the saved address. - This is required when we use the auto-increment feature. -*/ -void tms9995_device::return_with_address_copy() -{ - // Return from operand address derivation - m_instruction->program = m_caller; - MPC = m_caller_MPC; // will be increased on return - m_address = m_address_saved; - if (TRACE_DETAIL) logerror("tms9995: +++ return from operand address derivation (auto inc) +++\n"); - // no clock pulse -} - -/* - CRU support code - See common explanations in tms9900.c - - The TMS9995 CRU address space is larger than the CRU space of the TMS9900: - 0000-fffe (even addresses) instead of 0000-1ffe. Unlike the TMS9900, the - 9995 uses the data bus lines D0-D2 to indicate external operations. - - Internal CRU locations (read/write) - ----------------------------------- - 1EE0 Flag 0 Decrementer as event counter - 1EE2 Flag 1 Decrementer enable - 1EE4 Flag 2 Level 1 interrupt present (read only, also set when interrupt mask disallows interrupts) - 1EE6 Flag 3 Level 3 interrupt present (see above) - 1EE8 Flag 4 Level 4 interrupt present (see above) - ... - 1EFE Flag 15 - 1FDA MID flag (only indication, does not trigger when set) - - The TMS9995 allows for wait states during external CRU access. Therefore - we read one block of 8 bits in one go (as given by the MESS architecture) - but we do iterations for each bit, checking every time for the READY line - in the main loop. - - (write) - m_cru_output - m_cru_address - m_cru_value - m_count - -*/ - -#define CRUREADMASK 0x0fff -#define CRUWRITEMASK 0x7fff - -void tms9995_device::cru_output_operation() -{ - if (TRACE_CRU) logerror("tms9995: CRU output operation, address %04x, value %d\n", m_cru_address, m_cru_value & 0x01); - - if (m_cru_address == 0x1fda) - { - // [1], section 2.3.3.2.2: "setting the MID flag to one with a CRU instruction - // will not cause the MID interrupt to be requested." - m_check_ready = false; - m_mid_flag = (m_cru_value & 0x01); - } - else - { - if ((m_cru_address & 0xffe0) == 0x1ee0) - { - m_check_ready = false; - // FLAG2, FLAG3, and FLAG4 are read-only - if (TRACE_CRU) logerror("tms9995: set CRU address %04x to %d\n", m_cru_address, m_cru_value&1); - if ((m_cru_address != 0x1ee4) && (m_cru_address != 0x1ee6) && (m_cru_address != 0x1ee8)) - m_flag[(m_cru_address>>1)&0x000f] = (m_cru_value & 0x01); - } - else - { - // External access - m_check_ready = true; - } - } - - // All CRU write operations are visible to the outside world, even when we - // have internal access. This makes it possible to assign special - // functions to the internal flag bits which are realized outside - // of the CPU. However, no wait states are generated for internal - // accesses. ([1], section 2.3.3.2) - - m_cru->write_byte((m_cru_address >> 1)& CRUWRITEMASK, (m_cru_value & 0x01)); - m_cru_value >>= 1; - m_cru_address = (m_cru_address + 2) & 0xfffe; - m_count--; - - // Repeat this operation - m_pass = (m_count > 0)? 2 : 1; - pulse_clock(2); -} - -/* - Input: (read) - m_cru_multi_first - m_cru_address - Output: - m_cru_value (right-shifted; i.e. first bit is LSB of the 16 bit word, - also for byte operations) -*/ - -void tms9995_device::cru_input_operation() -{ - UINT16 crubit; - UINT8 crubyte; - - // Reading is different, since MESS uses 8 bit transfers - // We read 8 bits in one go, then iterate another min(n-1,7) times to allow - // for wait states. - - // read_byte for CRU delivers the first bit on the rightmost position - - int offset = (m_cru_address>>1) & 0x07; - - if (m_cru_first_read || m_cru_bits_left == 0) - { - // Read next 8 bits - // 00000000 0rrrrrrr r - // v - // ........ ........ X....... ........ - // - crubyte = m_cru->read_byte((m_cru_address >> 4)& CRUREADMASK); - if (TRACE_DETAIL) logerror("tms9995: Need to get next 8 bits (addresses %04x-%04x): %02x\n", (m_cru_address&0xfff0)+14, m_cru_address&0xfff0, crubyte); - m_cru_read = crubyte << 15; - m_cru_bits_left = 8; - - if (m_cru_first_read) - { - m_cru_read >>= offset; - m_cru_bits_left -= offset; - m_parity = 0; - m_cru_value = 0; - m_cru_first_read = false; - m_pass = m_count; - } - if (TRACE_DETAIL) logerror("tms9995: adjusted value for shift: %06x\n", m_cru_read); - } - - crubit = (m_cru_read & 0x8000); - m_cru_value = (m_cru_value >> 1) & 0x7fff; - - // During internal reading, the CRUIN line will be ignored. We emulate this - // by overwriting the bit which we got from outside. Also, READY is ignored. - if (m_cru_address == 0x1fda) - { - crubit = m_mid_flag? 0x8000 : 0x0000; - m_check_ready = false; - } - else - { - if ((m_cru_address & 0xffe0)==0x1ee0) - { - crubit = (m_flag[(m_cru_address>>1)&0x000f]==true)? 0x8000 : 0x0000; - m_check_ready = false; - } - else - { - m_check_ready = true; - } - } - - if (TRACE_CRU) logerror("tms9995: CRU input operation, address %04x, value %d\n", m_cru_address, (crubit & 0x8000)>>15); - - m_cru_value |= crubit; - if (crubit!=0) m_parity++; - - m_cru_address = (m_cru_address + 2) & 0xfffe; - m_cru_bits_left--; - - if (m_pass > 1) - { - m_cru_read >>= 1; - } - else - { - // This is the final shift. For both byte and word length transfers, - // the first bit is always m_cru_value & 0x0001. - m_cru_value >>= (16 - m_count); - } - pulse_clock(2); -} - -/* - Decrementer. -*/ -void tms9995_device::trigger_decrementer() -{ - if (m_starting_count_storage_register>0) // null will turn off the decrementer - { - m_decrementer_value--; - if (m_decrementer_value==0) - { - if (TRACE_DEC) logerror("tms9995: decrementer reached 0\n"); - m_decrementer_value = m_starting_count_storage_register; - if (m_flag[1]==true) - { - if (TRACE_DEC) logerror("tms9995: decrementer flags interrupt\n"); - m_flag[3] = true; - m_int_decrementer = true; - } - } - } -} - -/* - This is a switch to a subprogram. In terms of cycles - it does not take any time; execution continues with the first instruction - of the subprogram. - - input: - m_get_destination - m_instruction - WP - m_current_value - m_address - output: - m_source_value = m_current_value before invocation - m_current_value = m_address - m_address_add = 0 - m_lowbyte = false - m_get_destination = true - m_regnumber = register number - m_address = address of register - */ -void tms9995_device::operand_address_subprogram() -{ - UINT16 ircopy = m_instruction->IR; - if (m_get_destination) ircopy = ircopy >> 6; - - // Save the return program and position - m_caller = m_instruction->program; - m_caller_MPC = MPC; - - m_instruction->program = (UINT8*)operand_address_derivation; - MPC = (ircopy & 0x0030) >> 2; - m_regnumber = (ircopy & 0x000f); - m_address = (WP + (m_regnumber<<1)) & 0xffff; - - m_source_value = m_current_value; // will be overwritten when reading the destination - m_current_value = m_address; // needed for first case - - if (MPC==8) // Symbolic - { - if (m_regnumber != 0) - { - if (TRACE_DETAIL) logerror("tms9995: indexed addressing\n"); - MPC = 16; // indexed - } - else - { - if (TRACE_DETAIL) logerror("tms9995: symbolic addressing\n"); - m_address = PC; - PC = (PC + 2) & 0xfffe; - } - } - - m_get_destination = true; - m_mem_phase = 1; - m_address_add = 0; - MPC--; // will be increased in the mail loop - if (TRACE_DETAIL) logerror("tms9995: *** Operand address derivation; address=%04x; index=%d\n", m_address, MPC+1); -} - -/* - Used for register auto-increment. We have to save the address read from the - register content so that we can return it at the end. -*/ -void tms9995_device::increment_register() -{ - m_address_saved = m_current_value; // need a special return so we do not lose the value - m_current_value += m_instruction->byteop? 1 : 2; - m_address = (WP + (m_regnumber<<1)) & 0xffff; - m_mem_phase = 1; - pulse_clock(1); -} - -/* - Used for indexed addressing. We store the contents of the index register - in m_address_add which is set to 0 by default. Then we set the address - pointer to the PC location and advance it. -*/ -void tms9995_device::indexed_addressing() -{ - m_address_add = m_current_value; - m_address = PC; - PC = (PC + 2) & 0xfffe; - m_mem_phase = 1; - pulse_clock(1); -} - -void tms9995_device::set_immediate() -{ - // Need to determine the register address - m_address_saved = WP + ((m_instruction->IR & 0x000f)<<1); - m_address = PC; - m_source_value = m_current_value; // needed for AI, ANDI, ORI - PC = (PC + 2) & 0xfffe; - m_mem_phase = 1; -} - -/************************************************************************** - Status bit operations -**************************************************************************/ - -inline void tms9995_device::set_status_bit(int bit, bool state) -{ - if (state) ST |= bit; - else ST &= ~bit; - m_int_overflow = (m_check_overflow && bit == ST_OV && ((ST & ST_OE)!=0) && state == true); -} - -void tms9995_device::set_status_parity(UINT8 value) -{ - int count = 0; - for (int i=0; i < 8; i++) - { - if ((value & 0x80)!=0) count++; - value <<= 1; - } - set_status_bit(ST_OP, (count & 1)!=0); -} - -inline void tms9995_device::compare_and_set_lae(UINT16 value1, UINT16 value2) -{ - set_status_bit(ST_EQ, value1 == value2); - set_status_bit(ST_LH, value1 > value2); - set_status_bit(ST_AGT, (INT16)value1 > (INT16)value2); -} - -/************************************************************************** - ALU operations. The activities as implemented here are performed - during the internal operations of the CPU, according to the current - instruction. - - Some ALU operations are followed by the prefetch operation. In fact, - this prefetch happens in parallel to the ALU operation. In these - situations we do not pulse the clock here but leave this to the prefetch - operation. -**************************************************************************/ - -void tms9995_device::alu_nop() -{ - // Do nothing (or nothing that is externally visible) - pulse_clock(1); - return; -} - -void tms9995_device::alu_add_s_sxc() -{ - // We have the source operand value in m_source_value and the destination - // value in m_current_value - // The destination address is still in m_address - // Prefetch will not change m_current_value and m_address - - UINT32 dest_new = 0; - - switch (m_instruction->command) - { - case A: - case AB: - // When adding, a carry occurs when we exceed the 0xffff value. - dest_new = m_current_value + m_source_value; - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - - // If the result has a sign bit that is different from both arguments, we have an overflow - // (i.e. getting a negative value from two positive values and vice versa) - set_status_bit(ST_OV, ((dest_new ^ m_current_value) & (dest_new ^ m_source_value) & 0x8000)!=0); - break; - case S: - case SB: - dest_new = m_current_value + ((~m_source_value) & 0xffff) + 1; - // Subtraction means adding the 2s complement, so the carry bit - // is set whenever adding the 2s complement exceeds ffff - // In fact the CPU adds the one's complement, then adds a one. This - // explains why subtracting 0 sets the carry bit. - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - - // If the arguments have different sign bits and the result has a - // sign bit different from the destination value, we have an overflow - // e.g. value1 = 0x7fff, value2 = 0xffff; value1-value2 = 0x8000 - // or value1 = 0x8000, value2 = 0x0001; value1-value2 = 0x7fff - // value1 is the destination value - set_status_bit(ST_OV, (m_current_value ^ m_source_value) & (m_current_value ^ dest_new) & 0x8000); - break; - case SOC: - case SOCB: - dest_new = m_current_value | m_source_value; - break; - - case SZC: - case SZCB: - dest_new = m_current_value & ~m_source_value; - break; - } - - m_current_value = (UINT16)(dest_new & 0xffff); - - compare_and_set_lae((UINT16)(dest_new & 0xffff),0); - if (m_instruction->byteop) - { - set_status_parity((UINT8)(dest_new>>8)); - } - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); - // No clock pulse (will be done by prefetch) -} - -/* - Branch / Branch and link. We put the source address into the PC after - copying the PC into m_current_value. The address is R11. The B instruction - will just ignore these settings, but BL will use them. -*/ -void tms9995_device::alu_b() -{ - m_current_value = PC; - PC = m_address & 0xfffe; - m_address = WP + 22; -} - -/* - Branch and load workspace pointer. This is a branch to a subprogram with - context switch. -*/ -void tms9995_device::alu_blwp() -{ - int n = 1; - switch (m_instruction->state) - { - case 0: - // new WP in m_current_value - m_value_copy = WP; - WP = m_current_value & 0xfffe; - m_address_saved = m_address + 2; - m_address = WP + 30; - m_current_value = ST; - break; - case 1: - m_current_value = PC; - m_address = m_address - 2; - break; - case 2: - m_current_value = m_value_copy; // old WP - m_address = m_address - 2; - break; - case 3: - m_address = m_address_saved; - break; - case 4: - PC = m_current_value & 0xfffe; - n = 0; - if (TRACE_OP) logerror("tms9995: Context switch complete; WP=%04x, PC=%04x, ST=%04x\n", WP, PC, ST); - break; - } - m_instruction->state++; - pulse_clock(n); -} - -/* - Compare is similar to add, s, soc, szc, but we do not write a result. -*/ -void tms9995_device::alu_c() -{ - // We have the source operand value in m_source_value and the destination - // value in m_current_value - // The destination address is still in m_address - // Prefetch will not change m_current_value and m_address - if (m_instruction->byteop) - { - set_status_parity((UINT8)(m_source_value>>8)); - } - compare_and_set_lae(m_source_value, m_current_value); - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val1=%04x, val2=%04x)\n", ST, m_source_value, m_current_value); -} - -/* - Compare with immediate value. -*/ -void tms9995_device::alu_ci() -{ - // We have the register value in m_source_value, the register address in m_address_saved - // and the immediate value in m_current_value - compare_and_set_lae(m_source_value, m_current_value); - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val1=%04x, val2=%04x)\n", ST, m_source_value, m_current_value); -} - -void tms9995_device::alu_clr_seto() -{ - if (TRACE_OP) logerror("tms9995: clr/seto: Setting values for address %04x\n", m_address); - switch (m_instruction->command) - { - case CLR: - m_current_value = 0; - break; - case SETO: - m_current_value = 0xffff; - break; - } - // No clock pulse, as next instruction is prefetch -} - -/* - Unsigned division. -*/ -void tms9995_device::alu_divide() -{ - int n=1; - UINT32 uval32; - - bool overflow = true; - UINT16 value1; - - switch (m_instruction->state) - { - case 0: - m_source_value = m_current_value; - // Set address of register - m_address = WP + ((m_instruction->IR >> 5) & 0x001e); - m_address_copy = m_address; - break; - case 1: - // Value of register is in m_current_value - // We have an overflow when the quotient cannot be stored in 16 bits - // This is the case when the dividend / divisor >= 0x10000, - // or equivalently, dividend / 0x10000 >= divisor - - // Check overflow for unsigned DIV - if (m_current_value < m_source_value) // also if source=0 - { - MPC++; // skip the abort - overflow = false; - } - set_status_bit(ST_OV, overflow); - m_value_copy = m_current_value; // Save the high word - m_address = m_address + 2; - break; - case 2: - // W2 is in m_current_value - uval32 = (m_value_copy << 16) | m_current_value; - // Calculate - // The number of ALU cycles depends on the number of steps in - // the division algorithm. The number of cycles is between 1 and 16 - // As in TMS9900, this is a guess; it depends on the actual algorithm - // used in the chip. - - m_current_value = uval32 / m_source_value; - m_value_copy = uval32 % m_source_value; - m_address = m_address_copy; - - value1 = m_value_copy & 0xffff; - while (value1 != 0) - { - value1 = (value1 >> 1) & 0xffff; - n++; - } - - break; - case 3: - // now write the remainder - m_current_value = m_value_copy; - m_address = m_address + 2; - break; - } - m_instruction->state++; - pulse_clock(n); -} - -/* - Signed Division - We cannot handle this by the same ALU operation because we can NOT decide - whether there is an overflow before we have retrieved the whole 32 bit - word. Also, the overflow detection is pretty complicated for signed - division when done before the actual calculation. -*/ -void tms9995_device::alu_divide_signed() -{ - int n=1; - bool overflow = true; - UINT16 w1, w2, dwait; - INT16 divisor; - INT32 dividend; - - switch (m_instruction->state) - { - case 0: - // Got the source value (divisor) - m_source_value = m_current_value; - m_address = WP; // DIVS always uses R0,R1 - break; - case 1: - // Value of register is in m_current_value - m_value_copy = m_current_value; - m_address += 2; - break; - case 2: - // Now we have the dividend low word in m_current_value, - // the dividend high word in m_value_copy, and - // the divisor in m_source_value. - - w1 = m_value_copy; - w2 = m_current_value; - divisor = m_source_value; - dividend = w1 << 16 | w2; - - // Now check for overflow - // We need to go for four cases - // if the divisor is not 0 anyway - if (divisor != 0) - { - if (dividend >= 0) - { - if (divisor > 0) - { - overflow = (dividend > ((divisor<<15) - 1)); - } - else - { - overflow = (dividend > (((-divisor)<<15) + (-divisor) - 1)); - } - } - else - { - if (divisor > 0) - { - overflow = ((-dividend) > ((divisor<<15) + divisor - 1)); - } - else - { - overflow = ((-dividend) > (((-divisor)<<15) - 1)); - } - } - } - else - { - overflow = true; // divisor is 0 - } - set_status_bit(ST_OV, overflow); - if (!overflow) MPC++; // Skip the next microinstruction when there is no overflow - break; - case 3: - // We are here because there was no overflow - dividend = m_value_copy << 16 | m_current_value; - // Do the calculation - m_current_value = (UINT16)(dividend / (INT16)m_source_value); - m_value_copy = (UINT16)(dividend % (INT16)m_source_value); - m_address = WP; - - // As we have not implemented the real division algorithm we must - // simulate the number of steps required for calculating the result. - // This is just a guess. - dwait = m_value_copy; - while (dwait != 0) - { - dwait = (dwait >> 1) & 0xffff; - n++; - } - // go write the quotient into R0 - break; - case 4: - // Now write the remainder - m_current_value = m_value_copy; - m_address += 2; - n = 0; - break; - } - m_instruction->state++; - pulse_clock(n); -} - -/* - External operations. -*/ -void tms9995_device::alu_external() -{ - // Call some possibly attached external device - // A specific bit pattern is put on the data bus, and the CRUOUT line is - // pulsed. In our case we use a special callback function since we cannot - // emulate this behavior in this implementation. - - // Opcodes D012 value - // -----------------vvv------ - // IDLE = 0000 0011 0100 0000 - // RSET = 0000 0011 0110 0000 - // CKON = 0000 0011 1010 0000 - // CKOF = 0000 0011 1100 0000 - // LREX = 0000 0011 1110 0000 - - // Only IDLE has a visible effect on the CPU without external support: the - // CPU will stop execution until an interrupt occurs. CKON, CKOF, LREX have - // no effect without external support. Neither has RSET, it does *not* - // cause a reset of the CPU or of the remaining computer system. - // It only clears the interrupt mask and outputs the - // external code on the data bus. A special line decoder could then trigger - // a reset from outside. - - if (m_instruction->command == IDLE) - { - if (TRACE_OP) logerror("tms9995: Entering IDLE state\n"); - m_idle_state = true; - } - - if (m_instruction->command == RSET) - { - ST &= 0xfff0; - if (TRACE_OP) logerror("tms9995: RSET, new ST = %04x\n", ST); - } - - if (!m_external_operation.isnull()) m_external_operation((m_instruction->IR >> 5) & 0x07, 1, 0xff); -} - -/* - Logical compare and XOR -*/ -void tms9995_device::alu_f3() -{ - switch (m_instruction->state) - { - case 0: - // We have the contents of the source in m_current_value and its address - // in m_address - m_source_value = m_current_value; - // Get register address - m_address = WP + ((m_instruction->IR >> 5) & 0x001e); - break; - case 1: - // Register contents -> m_current_value - // Source contents -> m_source_value - if (m_instruction->command == COC) - { - set_status_bit(ST_EQ, (m_current_value & m_source_value) == m_source_value); - } - else - { - if (m_instruction->command == CZC) - { - set_status_bit(ST_EQ, (~m_current_value & m_source_value) == m_source_value); - } - else - { - // XOR - // The workspace register address is still in m_address - m_current_value = (m_current_value ^ m_source_value); - compare_and_set_lae(m_current_value, 0); - } - } - if (TRACE_STATUS) logerror("tms9995: ST = %04x\n", ST); - break; - } - m_instruction->state++; -} - -/* - Handles AI, ANDI, ORI. -*/ -void tms9995_device::alu_imm_arithm() -{ - UINT32 dest_new = 0; - - // We have the register value in m_source_value, the register address in m_address_saved - // and the immediate value in m_current_value - switch (m_instruction->command) - { - case AI: - dest_new = m_current_value + m_source_value; - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - - // If the result has a sign bit that is different from both arguments, we have an overflow - // (i.e. getting a negative value from two positive values and vice versa) - set_status_bit(ST_OV, ((dest_new ^ m_current_value) & (dest_new ^ m_source_value) & 0x8000)!=0); - break; - case ANDI: - dest_new = m_current_value & m_source_value; - break; - case ORI: - dest_new = m_current_value | m_source_value; - break; - } - - m_current_value = (UINT16)(dest_new & 0xffff); - compare_and_set_lae(m_current_value, 0); - m_address = m_address_saved; - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); -} - -/* - Handles all jump instructions. -*/ -void tms9995_device::alu_jump() -{ - bool cond = false; - INT8 displacement = (m_instruction->IR & 0xff); - - switch (m_instruction->command) - { - case JMP: - cond = true; - break; - case JLT: // LAECOP == x00xxx - cond = ((ST & (ST_AGT | ST_EQ))==0); - break; - case JLE: // LAECOP == 0xxxxx - cond = ((ST & ST_LH)==0); - break; - case JEQ: // LAECOP == xx1xxx - cond = ((ST & ST_EQ)!=0); - break; - case JHE: // LAECOP == 1x0xxx, 0x1xxx - cond = ((ST & (ST_LH | ST_EQ)) != 0); - break; - case JGT: // LAECOP == x1xxxx - cond = ((ST & ST_AGT)!=0); - break; - case JNE: // LAECOP == xx0xxx - cond = ((ST & ST_EQ)==0); - break; - case JNC: // LAECOP == xxx0xx - cond = ((ST & ST_C)==0); - break; - case JOC: // LAECOP == xxx1xx - cond = ((ST & ST_C)!=0); - break; - case JNO: // LAECOP == xxxx0x - cond = ((ST & ST_OV)==0); - break; - case JL: // LAECOP == 0x0xxx - cond = ((ST & (ST_LH | ST_EQ)) == 0); - break; - case JH: // LAECOP == 1xxxxx - cond = ((ST & ST_LH)!=0); - break; - case JOP: // LAECOP == xxxxx1 - cond = ((ST & ST_OP)!=0); - break; - } - - if (!cond) - { - if (TRACE_OP) logerror("tms9995: Jump condition false\n"); - } - else - { - if (TRACE_OP) logerror("tms9995: Jump condition true\n"); - PC = (PC + (displacement<<1)) & 0xfffe; - } -} - -/* - Implements LDCR. -*/ -void tms9995_device::alu_ldcr() -{ - switch (m_instruction->state) - { - case 0: - m_count = (m_instruction->IR >> 6) & 0x000f; - if (m_count==0) m_count = 16; - m_instruction->byteop = (m_count<9); - break; - case 1: - // We have read the byte or word into m_current_value. - compare_and_set_lae(m_current_value, 0); - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); - if (m_instruction->byteop) - { - m_current_value = (m_current_value>>8) & 0xff; - set_status_parity((UINT8)m_current_value); - } - m_cru_value = m_current_value; - m_address = WP + 24; - break; - case 2: - // Prepare CRU operation - m_cru_address = m_current_value; - break; - } - m_instruction->state++; - pulse_clock(1); -} - -/* - Implements LI. Almost everything has been done in the microprogram; - this part is reached with m_address_saved = register address, - and m_current_value = *m_address; -*/ -void tms9995_device::alu_li() -{ - // Retrieve the address of the register - // The immediate value is still in m_current_value - m_address = m_address_saved; - compare_and_set_lae(m_current_value, 0); - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); -} - -void tms9995_device::alu_limi_lwpi() -{ - // The immediate value is in m_current_value - if (m_instruction->command == LIMI) - { - ST = (ST & 0xfff0) | (m_current_value & 0x000f); - if (TRACE_OP) logerror("tms9995: LIMI sets ST = %04x\n", ST); - pulse_clock(1); // needs one more than LWPI - } - else - { - WP = m_current_value & 0xfffe; - if (TRACE_OP) logerror("tms9995: LWPI sets new WP = %04x\n", WP); - } -} - -/* - Load status and load workspace pointer. This is a TMS9995-specific - operation. -*/ -void tms9995_device::alu_lst_lwp() -{ - if (m_instruction->command==LST) - { - ST = m_current_value; - if (TRACE_OP) logerror("tms9995: new ST = %04x\n", ST); - pulse_clock(1); - } - else - { - WP = m_current_value & 0xfffe; - if (TRACE_OP) logerror("tms9995: new WP = %04x\n", WP); - } -} - -/* - The MOV operation on the TMS9995 is definitely more efficient than in the - TMS9900. As we have only 8 data bus lines we can read or write bytes - with only one cycle. The TMS9900 always has to read the memory word first - in order to write back a complete word, also when doing byte operations. -*/ -void tms9995_device::alu_mov() -{ - m_current_value = m_source_value; - if (m_instruction->byteop) - { - set_status_parity((UINT8)(m_current_value>>8)); - } - compare_and_set_lae(m_current_value, 0); - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); - // No clock pulse, as next instruction is prefetch -} - -/* - Unsigned and signed multiplication -*/ -void tms9995_device::alu_multiply() -{ - int n = 0; - UINT32 result = 0; - INT32 results = 0; - - if (m_instruction->command==MPY) - { - switch (m_instruction->state) - { - case 0: - // m_current_value <- multiplier (source) - m_source_value = m_current_value; - // m_address is the second multiplier (in a register) - m_address = ((m_instruction->IR >> 5) & 0x001e) + WP; - n = 1; - break; - case 1: - // m_current_value <- register content - result = (m_source_value & 0x0000ffff) * (m_current_value & 0x0000ffff); - m_current_value = (result >> 16) & 0xffff; - m_value_copy = result & 0xffff; - // m_address is still the register - n = 17; - break; - case 2: - m_address += 2; - m_current_value = m_value_copy; - // now write the lower 16 bit. - // If the register was R15, do not use R0 but continue writing after - // R15's address - break; - } - } - else - { - switch (m_instruction->state) - { - case 0: - // m_current_value <- multiplier (source) - m_source_value = m_current_value; - // m_address is the second multiplier (in R0) - m_address = WP; - n = 1; - break; - case 1: - // m_current_value <- register content - results = ((INT16)m_source_value) * ((INT16)m_current_value); - m_current_value = (results >> 16) & 0xffff; - m_value_copy = results & 0xffff; - // m_address is still the register - n = 16; - break; - case 2: - m_address += 2; - m_current_value = m_value_copy; - // now write the lower 16 bit. - break; - } - } - m_instruction->state++; - pulse_clock(n); -} - -void tms9995_device::alu_rtwp() -{ - switch (m_instruction->state) - { - case 0: - m_address = WP + 30; // R15 - pulse_clock(1); - break; - case 1: - ST = m_current_value; - m_address -= 2; // R14 - break; - case 2: - PC = m_current_value & 0xfffe; - m_address -= 2; // R13 - break; - case 3: - WP = m_current_value & 0xfffe; - - // Just for debugging purposes - m_servicing_interrupt = false; - - if (TRACE_OP) logerror("tms9995: RTWP restored old context (WP=%04x, PC=%04x, ST=%04x)\n", WP, PC, ST); - break; - } - m_instruction->state++; -} - -void tms9995_device::alu_sbo_sbz() -{ - INT8 displacement; - - if (m_instruction->state==0) - { - m_address = WP + 24; - } - else - { - m_cru_value = (m_instruction->command==SBO)? 1 : 0; - displacement = (INT8)(m_instruction->IR & 0xff); - m_cru_address = m_current_value + (displacement<<1); - m_count = 1; - } - m_instruction->state++; - pulse_clock(1); -} - -/* - Perform the shift operation -*/ -void tms9995_device::alu_shift() -{ - bool carry = false; - bool overflow = false; - UINT16 sign = 0; - UINT32 value; - int count; - - switch (m_instruction->state) - { - case 0: - // we have the value of the register in m_current_value - // Save it (we may have to read R0) - m_value_copy = m_current_value; - m_address_saved = m_address; - m_address = WP; - // store this in m_current_value where the R0 value will be put - m_current_value = (m_instruction->IR >> 4)& 0x000f; - if (m_current_value != 0) - { - // skip the next read operation - MPC++; - } - else - { - if (TRACE_DETAIL) logerror("tms9995: Shift operation gets count from R0\n"); - } - pulse_clock(1); - pulse_clock(1); - break; - - case 1: - count = m_current_value & 0x000f; // from the instruction or from R0 - if (count==0) count = 16; - - value = m_value_copy; - - // we are re-implementing the shift operations because we have to pulse - // the clock at each single shift anyway. - // Also, it is easier to implement the status bit setting. - // Note that count is never 0 - if (m_instruction->command == SRA) sign = value & 0x8000; - - for (int i=0; i < count; i++) - { - switch (m_instruction->command) - { - case SRL: - case SRA: - carry = ((value & 1)!=0); - value = (value >> 1) | sign; - break; - case SLA: - carry = ((value & 0x8000)!=0); - value <<= 1; - if (carry != ((value&0x8000)!=0)) overflow = true; - break; - case SRC: - carry = ((value & 1)!=0); - value = (value>>1) | (carry? 0x8000 : 0x0000); - break; - } - pulse_clock(1); - } - - m_current_value = value & 0xffff; - set_status_bit(ST_C, carry); - set_status_bit(ST_OV, overflow); - compare_and_set_lae(m_current_value, 0); - m_address = m_address_saved; // Register address - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); - break; - } - m_instruction->state++; -} - -/* - Handles ABS, DEC, DECT, INC, INCT, NEG, INV -*/ -void tms9995_device::alu_single_arithm() -{ - UINT32 dest_new = 0; - UINT32 src_val = m_current_value & 0x0000ffff; - UINT16 sign = 0; - bool check_ov = true; - - switch (m_instruction->command) - { - case ABS: - // LAECO (from original word!) - // O if >8000 - // C is always reset - set_status_bit(ST_OV, m_current_value == 0x8000); - set_status_bit(ST_C, false); - compare_and_set_lae(m_current_value, 0); - - if ((m_current_value & 0x8000)!=0) - { - dest_new = ((~src_val) & 0x0000ffff) + 1; - } - else - { - dest_new = src_val; - } - m_current_value = dest_new & 0xffff; - return; - case DEC: - // LAECO - // Carry for result value != 0xffff - // Overflow for result value == 0x7fff - dest_new = src_val + 0xffff; - sign = 0x8000; - break; - case DECT: - // Carry for result value != 0xffff / 0xfffe - // Overflow for result value = 0x7fff / 0x7ffe - dest_new = src_val + 0xfffe; - sign = 0x8000; - break; - case INC: - // LAECO - // Overflow for result value = 0x8000 - // Carry for result value = 0x0000 - dest_new = src_val + 1; - break; - case INCT: - // LAECO - // Overflow for result value = 0x8000 / 0x8001 - // Carry for result value = 0x0000 / 0x0001 - dest_new = src_val + 2; - break; - case INV: - // LAE - dest_new = ~src_val & 0xffff; - check_ov = false; - break; - case NEG: - // LAECO - // Overflow occurs for value=0x8000 - // Carry occurs for value=0 - dest_new = ((~src_val) & 0x0000ffff) + 1; - check_ov = false; - set_status_bit(ST_OV, src_val == 0x8000); - break; - case SWPB: - m_current_value = ((m_current_value << 8) | (m_current_value >> 8)) & 0xffff; - // I don't know what they are doing right now, but we lose a lot of cycles - // according to the spec (which can indeed be proved on a real system) - - // Maybe this command is used as a forced wait between accesses to the - // video system. Usually we have two byte writes to set an address in - // the VDP, with a SWPB in between. Most software for the TI-99/4A using - // the TMS9900 will run into trouble when executed on the TI-99/8 with - // the much faster TMS9995. So the SWPB may be used to as an intentional - // slowdown. - - // No status bits affected - pulse_clock(10); - return; - } - - if (check_ov) set_status_bit(ST_OV, ((src_val & 0x8000)==sign) && ((dest_new & 0x8000)!=sign)); - set_status_bit(ST_C, (dest_new & 0x10000) != 0); - m_current_value = dest_new & 0xffff; - compare_and_set_lae(m_current_value, 0); - - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); - // No clock pulse, as next instruction is prefetch -} - -/* - Store CRU. -*/ -void tms9995_device::alu_stcr() -{ - int n = 1; - switch (m_instruction->state) - { - case 0: - m_count = (m_instruction->IR >> 6) & 0x000f; - if (m_count == 0) m_count = 16; - m_instruction->byteop = (m_count < 9); - break; - case 1: - m_address_saved = m_address; - m_address = WP + 24; - break; - case 2: - m_cru_address = m_current_value; - m_cru_first_read = true; - break; - case 3: - // I don't know what is happening here, but it takes quite some time. - // May be shift operations. - m_current_value = m_cru_value; - m_address = m_address_saved; - compare_and_set_lae(m_current_value, 0); - n = 13; - if (m_instruction->byteop) - { - set_status_parity((UINT8)m_current_value); - m_current_value <<= 8; - } - else n += 8; - if (TRACE_STATUS) logerror("tms9995: ST = %04x (val=%04x)\n", ST, m_current_value); - break; - } - m_instruction->state++; - pulse_clock(n); -} - - -/* - Store status and store workspace pointer. We need to determine the - address of the register here. -*/ -void tms9995_device::alu_stst_stwp() -{ - m_address = WP + ((m_instruction->IR & 0x000f)<<1); - m_current_value = (m_instruction->command==STST)? ST : WP; -} - -/* - Test CRU bit. -*/ -void tms9995_device::alu_tb() -{ - INT8 displacement; - - switch (m_instruction->state) - { - case 0: - m_address = WP + 24; - pulse_clock(1); - break; - case 1: - displacement = (INT8)(m_instruction->IR & 0xff); - m_cru_address = m_current_value + (displacement<<1); - m_cru_first_read = true; - m_count = 1; - pulse_clock(1); - break; - case 2: - set_status_bit(ST_EQ, m_cru_value!=0); - if (TRACE_STATUS) logerror("tms9995: ST = %04x\n", ST); - break; - } - m_instruction->state++; -} - -/* - Execute. This operation is substituted after reading the word at the - given address. -*/ -void tms9995_device::alu_x() -{ - // We have the word in m_current_value. This word must now be decoded - // as if it has been acquired by the normal procedure. - decode(m_current_value); - pulse_clock(1); - - // Switch to the prefetched and decoded instruction - next_command(); -} - -/* - XOP operation. -*/ -void tms9995_device::alu_xop() -{ - switch (m_instruction->state) - { - case 0: - // we have the source address in m_address - m_address_saved = m_address; - // Format is xxxx xxnn nnxx xxxx - m_address = 0x0040 + ((m_instruction->IR & 0x03c0)>>4); - pulse_clock(1); - break; - case 1: - // m_current_value is new WP - m_value_copy = WP; // store this for later - WP = m_current_value & 0xfffe; - m_address = WP + 0x0016; // Address of new R11 - m_current_value = m_address_saved; - pulse_clock(1); - break; - case 2: - m_address = WP + 0x001e; - m_current_value = ST; - pulse_clock(1); - break; - case 3: - m_address = WP + 0x001c; - m_current_value = PC; - pulse_clock(1); - break; - case 4: - m_address = WP + 0x001a; - m_current_value = m_value_copy; - pulse_clock(1); - break; - case 5: - m_address = 0x0042 + ((m_instruction->IR & 0x03c0)>>4); - pulse_clock(1); - break; - case 6: - PC = m_current_value & 0xfffe; - set_status_bit(ST_X, true); - break; - } - m_instruction->state++; -} - -/* - Handle an interrupt. The behavior as implemented here follows - exactly the flowchart in [1] -*/ -void tms9995_device::alu_int() -{ - int pulse = 1; - - switch (m_instruction->state) - { - case 0: - PC = (PC - 2) & 0xfffe; - m_address_saved = m_address; - if (TRACE_INTD) logerror("tms9995: interrupt service (0): Prepare to read vector\n"); - break; - case 1: - pulse = 2; // two cycles (with the one at the end) - m_source_value = WP; // old WP - WP = m_current_value & 0xfffe; // new WP - m_current_value = ST; - m_address = (WP + 30)&0xfffe; - if (TRACE_INTD) logerror("tms9995: interrupt service (1): Read new WP = %04x, save ST to %04x\n", WP, m_address); - break; - case 2: - m_address = (WP + 28)&0xfffe; - m_current_value = PC; - if (TRACE_INTD) logerror("tms9995: interrupt service (2): Save PC to %04x\n", m_address); - break; - case 3: - m_address = (WP + 26)&0xfffe; - m_current_value = m_source_value; // old WP - if (TRACE_INTD) logerror("tms9995: interrupt service (3): Save WP to %04x\n", m_address); - break; - case 4: - m_address = (m_address_saved + 2) & 0xfffe; - if (TRACE_INTD) logerror("tms9995: interrupt service (4): Read PC from %04x\n", m_address); - break; - case 5: - PC = m_current_value & 0xfffe; - ST = (ST & 0xfe00) | m_intmask; - if (TRACE_INTD) logerror("tms9995: interrupt service (5): Context switch complete; WP=%04x, PC=%04x, ST=%04x\n", WP, PC, ST); - - if (((m_int_pending & PENDING_MID)!=0) && m_nmi_active) - { - if (TRACE_INTD) logerror("tms9995: interrupt service (6): NMI active after context switch\n"); - m_int_pending &= ~PENDING_MID; - m_address = 0xfffc; - m_intmask = 0; - MPC = 0; // redo the interrupt service for the NMI - } - else - { - if (m_from_reset) - { - if (TRACE_INTD) logerror("tms9995: interrupt service (6): RESET completed\n"); - // We came from the RESET interrupt - m_from_reset = false; - ST &= 0x01ff; - m_mid_flag = false; - m_mid_active = false; - // FLAG0 and FLAG1 are also set to zero after RESET ([1], sect. 2.3.1.2.2) - for (int i=0; i < 5; i++) m_flag[i] = false; - m_check_hold = true; - } - } - pulse = 0; - break; - - // If next instruction is MID opcode we will detect this in command_completed - } - m_instruction->state++; - pulse_clock(pulse); -} - -/**************************************************************************/ -UINT32 tms9995_device::execute_min_cycles() const -{ - return 2; -} - -UINT32 tms9995_device::execute_max_cycles() const -{ - return 44; -} - -UINT32 tms9995_device::execute_input_lines() const -{ - return 2; -} - -UINT32 tms9995_device::disasm_min_opcode_bytes() const -{ - return 2; -} - -UINT32 tms9995_device::disasm_max_opcode_bytes() const -{ - return 6; -} - -offs_t tms9995_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) -{ - extern CPU_DISASSEMBLE( tms9995 ); - return CPU_DISASSEMBLE_NAME(tms9995)(this, buffer, pc, oprom, opram, options); -} - - -const device_type TMS9995 = &device_creator<tms9995_device>; -const device_type TMS9995_MP9537 = &device_creator<tms9995_mp9537_device>; diff --git a/src/emu/cpu/tms9900/tms9995.h b/src/emu/cpu/tms9900/tms9995.h deleted file mode 100644 index 81e2e560505..00000000000 --- a/src/emu/cpu/tms9900/tms9995.h +++ /dev/null @@ -1,446 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - tms9995.h - - See tms9995.c for documentation - Also see tms9900.h for types of TMS99xx processors. -*/ - -#ifndef __TMS9995_H__ -#define __TMS9995_H__ - -#include "emu.h" -#include "debugger.h" -#include "tms99com.h" - -// device type definition -extern const device_type TMS9995; -extern const device_type TMS9995_MP9537; - -enum -{ - INT_9995_RESET = 0, - INT_9995_INTREQ = 1, - INT_9995_INT1 = 2, - INT_9995_INT4 = 3 -}; - -#define MCFG_TMS9995_EXTOP_HANDLER( _extop) \ - devcb = &tms9995_device::static_set_extop_callback( *device, DEVCB_##_extop ); - -#define MCFG_TMS9995_IAQ_HANDLER( _iaq ) \ - devcb = &tms9995_device::static_set_iaq_callback( *device, DEVCB_##_iaq ); - -#define MCFG_TMS9995_CLKOUT_HANDLER( _clkout ) \ - devcb = &tms9995_device::static_set_clkout_callback( *device, DEVCB_##_clkout ); - -#define MCFG_TMS9995_HOLDA_HANDLER( _holda ) \ - devcb = &tms9995_device::static_set_holda_callback( *device, DEVCB_##_holda ); - -#define MCFG_TMS9995_DBIN_HANDLER( _dbin ) \ - devcb = &tms9995_device::static_set_dbin_callback( *device, DEVCB_##_dbin ); - -#define MCFG_TMS9995_ENABLE_OVINT( _ovint ) \ - downcast<tms9995_device*>(device)->set_overflow_interrupt( _ovint ); - - -class tms9995_device : public cpu_device -{ -public: - tms9995_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock); - tms9995_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, const char *source); - - // READY input line. When asserted (high), the memory is ready for data exchange. - // We chose to use a direct method instead of a delegate to keep performance - // footprint low; this method may be called very frequently. - void set_ready(int state); - - // HOLD input line. When asserted (low), the CPU is requested to release the - // data and address bus and enter the HOLD state. The entrance of this state - // is acknowledged by the HOLDA output line. - void set_hold(int state); - - // Callbacks - template<class _Object> static devcb_base &static_set_extop_callback(device_t &device, _Object object) { return downcast<tms9995_device &>(device).m_external_operation.set_callback(object); } - template<class _Object> static devcb_base &static_set_iaq_callback(device_t &device, _Object object) { return downcast<tms9995_device &>(device).m_iaq_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_clkout_callback(device_t &device, _Object object) { return downcast<tms9995_device &>(device).m_clock_out_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_holda_callback(device_t &device, _Object object) { return downcast<tms9995_device &>(device).m_holda_line.set_callback(object); } - template<class _Object> static devcb_base &static_set_dbin_callback(device_t &device, _Object object) { return downcast<tms9995_device &>(device).m_dbin_line.set_callback(object); } - - // For debugger access - UINT8 debug_read_onchip_memory(offs_t addr) { return m_onchip_memory[addr & 0xff]; }; - bool is_onchip(offs_t addrb) { return (((addrb & 0xff00)==0xf000 && (addrb < 0xf0fc)) || ((addrb & 0xfffc)==0xfffc)) && !m_mp9537; } - - void set_overflow_interrupt( int enable ) { m_check_overflow = (enable!=0); } - -protected: - // device-level overrides - virtual void device_start(); - virtual void device_stop(); - virtual void device_reset(); - - // device_execute_interface overrides - virtual UINT32 execute_min_cycles() const; - virtual UINT32 execute_max_cycles() const; - virtual UINT32 execute_input_lines() const; - virtual void execute_set_input(int irqline, int state); - virtual void execute_run(); - - // device_disasm_interface overrides - virtual UINT32 disasm_min_opcode_bytes() const; - virtual UINT32 disasm_max_opcode_bytes() const; - virtual offs_t disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options); - - const address_space_config* memory_space_config(address_spacenum spacenum) const; - - UINT64 execute_clocks_to_cycles(UINT64 clocks) const { return clocks / 4.0; } - UINT64 execute_cycles_to_clocks(UINT64 cycles) const { return cycles * 4.0; } - - // Variant of the TMS9995 without internal RAM and decrementer - bool m_mp9537; - -private: - // State / debug management - UINT16 m_state_any; - static const char* s_statename[]; - void state_import(const device_state_entry &entry); - void state_export(const device_state_entry &entry); - void state_string_export(const device_state_entry &entry, std::string &str); - UINT16 read_workspace_register_debug(int reg); - void write_workspace_register_debug(int reg, UINT16 data); - - // TMS9995 hardware registers - UINT16 WP; // Workspace pointer - UINT16 PC; // Program counter - UINT16 ST; // Status register - - // The TMS9995 has a prefetch feature which causes a wrong display of the PC. - // We use this additional member for the debugger only. - UINT16 PC_debug; - - // 256 bytes of onchip memory - UINT8 m_onchip_memory[256]; - - const address_space_config m_program_config; - const address_space_config m_io_config; - address_space* m_prgspace; - address_space* m_cru; - - - // Processor states - bool m_idle_state; - bool m_nmi_state; - bool m_irq_state; - bool m_hold_state; - - // READY handling. The READY line is operated before the clock - // pulse falls. As the ready line is only set once in this emulation we - // keep the level in a buffer (like a latch) - bool m_ready_bufd; // buffered state - bool m_ready; // sampled value - - // Auto-wait state generation - bool m_request_auto_wait_state; - bool m_auto_wait; - - // Cycle counter - int m_icount; - - // Phase of the memory access - int m_mem_phase; - - // Check the READY line? - bool m_check_ready; - - // Check the HOLD line - bool m_check_hold; - - // For multi-pass operations. For instance, memory word accesses are - // executed as two consecutive byte accesses. CRU accesses are repeated - // single-bit accesses. - int m_pass; - - // For parity operations - int m_parity; - - // For Format 1 instruction; determines whether the next operand address - // derivation is for the source or address operand - bool m_get_destination; - - // Used for situations when a command is byte-oriented, but the memory access - // must be word-oriented. Example: MOVB *R1,R0; we must read the full word - // from R1 to get the address. - bool m_word_access; - - // Interrupt handling - bool m_nmi_active; - bool m_int1_active; - bool m_int4_active; - bool m_int_decrementer; - bool m_int_overflow; - - bool m_reset; - bool m_from_reset; - bool m_mid_flag; - bool m_mid_active; - - int m_decrementer_clkdiv; - bool m_servicing_interrupt; - - // Flag field - int m_int_pending; - - // The TMS9995 is capable of raising an internal interrupt on - // arithmetic overflow, depending on the status register Overflow Enable bit. - // However, the specs also say that this feature is non-functional in the - // currently available chip. Thus we have an option to turn it off so that - // software will not change its behavior on overflows. - bool m_check_overflow; - - // Service pending interrupts - void service_interrupt(); - - // Issue clock pulses. The TMS9995 uses one (output) clock cycle per machine cycle. - inline void pulse_clock(int count); - - // Signal the hold state via the external line - inline void set_hold_state(bool state); - - // Only used for the DIV(S) operations. It seems sufficient to let the - // command terminate at this point, so this method just calls command_terminated. - void abort_operation(void); - - // Decode the given 16-bit value which has been retrieved by a prefetch or - // during an X operation. - void decode(UINT16 inst); - - // Store the interrupt mask part of the ST. This is used when processing - // an interrupt, passing the new mask from the service_interrupt part to - // the program part. - int m_intmask; - - // Stored address - UINT16 m_address; - - // Stores the recently read word or the word to be written - UINT16 m_current_value; - - // Stores the value of the source operand in multi-operand instructions - UINT16 m_source_value; - - // During indexed addressing, this value is added to get the final address value. - UINT16 m_address_add; - - // During indirect/auto-increment addressing, this copy of the address must - // be preserved while writing the new value to the register. - UINT16 m_address_saved; - - // Another copy of the address - UINT16 m_address_copy; - - // Copy of the value - UINT16 m_value_copy; - - // Stores the recent register number. Only used to pass the register - // number during the operand address derivation. - int m_regnumber; - - // Stores the number of bits or shift operations - int m_count; - - // ============== Decrementer ======================= - void trigger_decrementer(); - - // Start value - UINT16 m_starting_count_storage_register; - - // Current decrementer value. - UINT16 m_decrementer_value; - - // ============== CRU support ====================== - - UINT16 m_cru_address; - UINT16 m_cru_value; - bool m_cru_first_read; - int m_cru_bits_left; - UINT32 m_cru_read; - - // CPU-internal CRU flags - bool m_flag[16]; - - // ============== Prefetch support ===================== - - struct decoded_instruction - { - UINT16 IR; - UINT16 command; - const UINT8* program; - bool byteop; - int state; - }; - - int m_instindex; - - // We implement the prefetch mechanism by two separate datasets for - // the decoded commands. When the previous command has completed, the - // pointer is just switched to the other one. - tms9995_device::decoded_instruction m_decoded[2]; - tms9995_device::decoded_instruction* m_instruction; - - // ================ Microprogram support ======================== - - // Set up lookup table - void build_command_lookup_table(); - - // Sequence of micro-operations - typedef const UINT8* microprogram; - - // Method pointer - typedef void (tms9995_device::*ophandler)(void); - - // Opcode list entry - struct tms_instruction - { - UINT16 opcode; - int id; - int format; - microprogram prog; // Microprogram - }; - - // Lookup table entry - struct lookup_entry - { - lookup_entry *next_digit; - const tms_instruction *entry; - }; - - // Pointer to the lookup table; the entry point for searching the command - lookup_entry* m_command_lookup_table; - - // List of allocated tables (used for easy clean-up on exit) - lookup_entry* m_lotables[32]; - - // List of pointers for micro-operations - static const tms9995_device::ophandler s_microoperation[]; - - static const tms9995_device::tms_instruction s_command[]; - - // Micro-operation program counter (as opposed to the program counter PC) - int MPC; - - // Calling microprogram (used when data derivation is called) - const UINT8* m_caller; - int m_caller_MPC; - - // Table of microprograms - static const microprogram mp_table[]; - - // Used to display the number of consumed cycles in the log. - int m_first_cycle; - - // Status register update - inline void set_status_bit(int bit, bool state); - inline void compare_and_set_lae(UINT16 value1, UINT16 value2); - void set_status_parity(UINT8 value); - - // Micro-operation declarations - void int_prefetch_and_decode(); - void prefetch_and_decode(); - void mem_read(); - void mem_write(); - inline void word_read(); - inline void word_write(); - void operand_address_subprogram(); - void increment_register(); - void indexed_addressing(); - void set_immediate(); - void return_with_address(); - void return_with_address_copy(); - void cru_input_operation(); - void cru_output_operation(); - void command_completed(); - void next_command(); - - // ALU operations for specific commands - void alu_nop(); - void alu_add_s_sxc(); - void alu_b(); - void alu_blwp(); - void alu_c(); - void alu_ci(); - void alu_clr_seto(); - void alu_divide(); - void alu_divide_signed(); - void alu_external(); - void alu_f3(); - void alu_imm_arithm(); - void alu_jump(); - void alu_ldcr(); - void alu_li(); - void alu_limi_lwpi(); - void alu_lst_lwp(); - void alu_mov(); - void alu_multiply(); - void alu_multiply_signed(); - void alu_rtwp(); - void alu_sbo_sbz(); - void alu_shift(); - void alu_single_arithm(); - void alu_stcr(); - void alu_stst_stwp(); - void alu_tb(); - void alu_x(); - void alu_xop(); - void alu_int(); - - // ================ Connections ==================== - - // Trigger external operation. This is achieved by putting a special value in - // the most significant three bits of the data bus and pulsing the CRUCLK line. - // Accordingly, we have - // - // D0 D1 D2 - // 0 0 0 normal CRU access - // 0 1 0 IDLE - // 0 1 1 RSET - // 1 0 1 CKON - // 1 1 0 CKOF - // 1 1 1 LREX - // - // We could realize this via the CRU access as well, but the data bus access - // is not that simple to emulate. For the sake of homogenity between the - // chip emulations we use a dedicated callback. - devcb_write8 m_external_operation; - - // Signal to the outside world that we are now getting an instruction (IAQ). - // In the real hardware this line is shared with the HOLDA line, and the - // /MEMEN line is used to decide which signal we have on the line. We do not - // emulate the /MEMEN line, so we have to use two separate lines. - devcb_write_line m_iaq_line; - - // Clock output. - devcb_write_line m_clock_out_line; - - // Asserted when the CPU is in a HOLD state - devcb_write_line m_holda_line; - - // DBIN line. When asserted (high), the CPU has disabled the data bus output buffers. - devcb_write_line m_dbin_line; -}; - - -/* - Variant of the TMS9995 without on-chip RAM; used in the TI-99/8 console -*/ -class tms9995_mp9537_device : public tms9995_device -{ -public: - tms9995_mp9537_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) - : tms9995_device(mconfig, TMS9995_MP9537, "TMS9995-MP9537", tag, owner, clock, "tms9995_mp9537", __FILE__) - { - m_mp9537 = true; - } -}; - -#endif /* __TMS9995_H__ */ diff --git a/src/emu/cpu/tms9900/tms99com.h b/src/emu/cpu/tms9900/tms99com.h deleted file mode 100644 index 3a1aeeb57ee..00000000000 --- a/src/emu/cpu/tms9900/tms99com.h +++ /dev/null @@ -1,93 +0,0 @@ -// license:BSD-3-Clause -// copyright-holders:Michael Zapf -/* - Common definitions for all TMS processors - - Types of TMS99xx processors: - TI990/9 Early implementation, used in a few real-world applications, 1974 - very similar to mapper-less 990/10 and tms9900, but the Load - process is different - - TI990/10 Original multi-chip implementation for minicomputer systems, 1975 - - TI990/12 Multi-chip implementation, faster than 990/10. Huge instruction set - - TMS9900 Mono-chip implementation, 1976. Used in the TI-99/4(A) computer. - - TMS9940 Microcontroller with 2kb ROM, 128b RAM, decrementer, CRU bus, 1979 - - TMS9980 8-bit variant of tms9900. Two distinct chips actually : tms9980a - and tms9981 with an extra clock and simplified power supply - - TMS9985 9940 with 8kb ROM, 256b RAM, and a 8-bit external bus, c. 1978 (never released) - - TMS9989 Improved 9980, used in military hardware. - - SBP68689 Improved 9989, built as an ASIC as 9989 was running scarce - - TMS9995 TMS9985-like, with many improvements (but no ROM). Used in the - TI-99/8 prototype and the Geneve computer. - - TMS99000 Improved mono-chip implementation, meant to replace 990/10, 1981 - TMS99105 This chip is available in several variants which are similar - TMS99110 but emulate additional instructions, thanks to the so-called - macrostore feature. - - In this implementation we only consider TMS9900, 9980, and 9995. The - remaining types are implemented on an own code base as they introduce - significant changes (e.g. privileged mode, address mapper). -*/ - -#ifndef __TMS99COMMON_H__ -#define __TMS99COMMON_H__ - -#define MCFG_TMS99xx_ADD(_tag, _device, _clock, _prgmap, _iomap ) \ - MCFG_DEVICE_ADD(_tag, _device, _clock) \ - MCFG_DEVICE_PROGRAM_MAP(_prgmap) \ - MCFG_DEVICE_IO_MAP(_iomap) - -#define MCFG_TMS99xx_EXTOP_HANDLER( _extop) \ - devcb = &tms99xx_device::static_set_extop_callback( *device, DEVCB_##_extop ); - -#define MCFG_TMS99xx_INTLEVEL_HANDLER( _intlevel ) \ - devcb = &tms99xx_device::static_set_intlevel_callback( *device, DEVCB_##_intlevel ); - -#define MCFG_TMS99xx_IAQ_HANDLER( _iaq ) \ - devcb = &tms99xx_device::static_set_iaq_callback( *device, DEVCB_##_iaq ); - -#define MCFG_TMS99xx_CLKOUT_HANDLER( _clkout ) \ - devcb = &tms99xx_device::static_set_clkout_callback( *device, DEVCB_##_clkout ); - -#define MCFG_TMS99xx_WAIT_HANDLER( _wait ) \ - devcb = &tms99xx_device::static_set_wait_callback( *device, DEVCB_##_wait ); - -#define MCFG_TMS99xx_HOLDA_HANDLER( _holda ) \ - devcb = &tms99xx_device::static_set_holda_callback( *device, DEVCB_##_holda ); - -#define MCFG_TMS99xx_DBIN_HANDLER( _dbin ) \ - devcb = &tms99xx_device::static_set_dbin_callback( *device, DEVCB_##_dbin ); - -enum -{ - TI990_10_ID = 1, - TMS9900_ID = 3, - TMS9940_ID = 4, - TMS9980_ID = 5, - TMS9985_ID = 6, - TMS9989_ID = 7, - TMS9995_ID = 9, - TMS99000_ID = 10, - TMS99105A_ID = 11, - TMS99110A_ID = 12 -}; - -enum -{ - IDLE_OP = 2, - RSET_OP = 3, - CKOF_OP = 5, - CKON_OP = 6, - LREX_OP = 7 -}; - -#endif /* __TMS99COMMON_H__ */ |