diff options
Diffstat (limited to 'src/devices/cpu/nec/nec.c')
-rw-r--r-- | src/devices/cpu/nec/nec.c | 541 |
1 files changed, 541 insertions, 0 deletions
diff --git a/src/devices/cpu/nec/nec.c b/src/devices/cpu/nec/nec.c new file mode 100644 index 00000000000..6c53cfa677a --- /dev/null +++ b/src/devices/cpu/nec/nec.c @@ -0,0 +1,541 @@ +// license:BSD-3-Clause +// copyright-holders:Bryan McPhail +/**************************************************************************** + + NEC V20/V30/V33 emulator + + --------------------------------------------- + + V20 = uPD70108 = 8-bit data bus @ 5MHz or 8MHz + V20HL = uPD70108H = V20 with EMS support (24-bit address bus) + + V25 = uPD70320 = V20 with on-chip features: + - 256 bytes on-chip RAM + - 8 register banks + - 4-bit input port + - 20-bit I/O port + - 2 channel serial interface + - interrupt controller + - 2 channel DMA controller + - 2 channel 16-bit timer + - new instructions: BTCLR, RETRBI, STOP, BRKCS, TSKSW, + MOVSPA, MOVSPB + + V25+ = uPD70325 = V25 @ 8MHz or 10MHz plus changes: + - faster DMA + - improved serial interface + + --------------------------------------------- + + V30 = uPD70116 = 16-bit data bus version of V20 + V30HL = uPD70116H = 16-bit data bus version of V20HL + V30MX = V30HL with separate address and data busses + + V35 = uPD70330 = 16-bit data bus version of V25 + + V35+ = uPD70335 = 16-bit data bus version of V25+ + + --------------------------------------------- + + V40 = uPD70208 = 8-bit data bus @ 10MHz + V40HL = uPD70208H = V40 with support up to 20Mhz + + --------------------------------------------- + + V50 = uPD70216 = 16-bit data bus version of V40 + V50HL = uPD70216H = 16-bit data bus version of V40HL + + --------------------------------------------- + + V41 = uPD70270 + + V51 = uPD70280 + + + + V33A = uPD70136A (interrupt vector #s compatible with x86) + V53A = uPD70236A + + + + Instruction differences: + V20, V30, V40, V50 have dedicated emulation instructions + (BRKEM, RETEM, CALLN) + + V33 / V33A has dedicated address mode instructions (V53 / V53A are based on those cores with extra peripherals) + (BRKXA, RETXA) + + + + (Re)Written June-September 2000 by Bryan McPhail (mish@tendril.co.uk) based + on code by Oliver Bergmann (Raul_Bloodworth@hotmail.com) who based code + on the i286 emulator by Fabrice Frances which had initial work based on + David Hedley's pcemu(!). + + This new core features 99% accurate cycle counts for each processor, + there are still some complex situations where cycle counts are wrong, + typically where a few instructions have differing counts for odd/even + source and odd/even destination memory operands. + + Flag settings are also correct for the NEC processors rather than the + I86 versions. + + Changelist: + + 22/02/2003: + Removed cycle counts from memory accesses - they are certainly wrong, + and there is already a memory access cycle penalty in the opcodes + using them. + + Fixed save states. + + Fixed ADJBA/ADJBS/ADJ4A/ADJ4S flags/return values for all situations. + (Fixes bugs in Geostorm and Thunderblaster) + + Fixed carry flag on NEG (I thought this had been fixed circa Mame 0.58, + but it seems I never actually submitted the fix). + + Fixed many cycle counts in instructions and bug in cycle count + macros (odd word cases were testing for odd instruction word address + not data address). + + Todo! + Double check cycle timing is 100%. + +****************************************************************************/ + +#include "emu.h" +#include "debugger.h" + +typedef UINT8 BOOLEAN; +typedef UINT8 BYTE; +typedef UINT16 WORD; +typedef UINT32 DWORD; + +#include "nec.h" +#include "necpriv.h" + +const device_type V20 = &device_creator<v20_device>; +const device_type V30 = &device_creator<v30_device>; +const device_type V33 = &device_creator<v33_device>; +const device_type V33A =&device_creator<v33a_device>; + + + +nec_common_device::nec_common_device(const machine_config &mconfig, device_type type, const char *name, const char *tag, device_t *owner, UINT32 clock, const char *shortname, bool is_16bit, offs_t fetch_xor, UINT8 prefetch_size, UINT8 prefetch_cycles, UINT32 chip_type) + : cpu_device(mconfig, type, name, tag, owner, clock, shortname, __FILE__) + , m_program_config("program", ENDIANNESS_LITTLE, is_16bit ? 16 : 8, 20, 0) + , m_io_config("io", ENDIANNESS_LITTLE, is_16bit ? 16 : 8, 16, 0) + , m_fetch_xor(fetch_xor) + , m_prefetch_size(prefetch_size) + , m_prefetch_cycles(prefetch_cycles) + , m_chip_type(chip_type) +{ +} + + +v20_device::v20_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nec_common_device(mconfig, V20, "V20", tag, owner, clock, "v20", false, 0, 4, 4, V20_TYPE) +{ +} + + +v30_device::v30_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nec_common_device(mconfig, V30, "V30", tag, owner, clock, "v30", true, BYTE_XOR_LE(0), 6, 2, V30_TYPE) +{ +} + + +/* FIXME: Need information about prefetch size and cycles for V33. + * complete guess below, nbbatman will not work + * properly without. */ +v33_device::v33_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nec_common_device(mconfig, V33, "V33", tag, owner, clock, "v33", true, BYTE_XOR_LE(0), 6, 1, V33_TYPE) +{ +} + + +v33a_device::v33a_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : nec_common_device(mconfig, V33A, "V33A", tag, owner, clock, "v33A", true, BYTE_XOR_LE(0), 6, 1, V33_TYPE) +{ +} + + +offs_t nec_common_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) +{ + extern CPU_DISASSEMBLE( nec ); + return CPU_DISASSEMBLE_NAME(nec)(this, buffer, pc, oprom, opram, options); +} + + +void nec_common_device::prefetch() +{ + m_prefetch_count--; +} + +void nec_common_device::do_prefetch(int previous_ICount) +{ + int diff = previous_ICount - (int) m_icount; + + /* The implementation is not accurate, but comes close. + * It does not respect that the V30 will fetch two bytes + * at once directly, but instead uses only 2 cycles instead + * of 4. There are however only very few sources publicly + * available and they are vague. + */ + while (m_prefetch_count<0) + { + m_prefetch_count++; + if (diff>m_prefetch_cycles) + diff -= m_prefetch_cycles; + else + m_icount -= m_prefetch_cycles; + } + + if (m_prefetch_reset) + { + m_prefetch_count = 0; + m_prefetch_reset = 0; + return; + } + + while (diff>=m_prefetch_cycles && m_prefetch_count < m_prefetch_size) + { + diff -= m_prefetch_cycles; + m_prefetch_count++; + } + +} + +UINT8 nec_common_device::fetch() +{ + prefetch(); + return m_direct->read_byte((Sreg(PS)<<4)+m_ip++, m_fetch_xor); +} + +UINT16 nec_common_device::fetchword() +{ + UINT16 r = FETCH(); + r |= (FETCH()<<8); + return r; +} + +#include "necinstr.h" +#include "necmacro.h" +#include "necea.h" +#include "necmodrm.h" + +static UINT8 parity_table[256]; + +UINT8 nec_common_device::fetchop() +{ + prefetch(); + return m_direct->read_byte(( Sreg(PS)<<4)+m_ip++, m_fetch_xor); +} + + + +/***************************************************************************/ + +void nec_common_device::device_reset() +{ + memset( &m_regs.w, 0, sizeof(m_regs.w)); + + m_ip = 0; + m_TF = 0; + m_IF = 0; + m_DF = 0; + m_MF = 1; // brkem should set to 0 when implemented + m_SignVal = 0; + m_AuxVal = 0; + m_OverVal = 0; + m_ZeroVal = 1; + m_CarryVal = 0; + m_ParityVal = 1; + m_pending_irq = 0; + m_nmi_state = 0; + m_irq_state = 0; + m_poll_state = 1; + m_halted = 0; + + Sreg(PS) = 0xffff; + Sreg(SS) = 0; + Sreg(DS0) = 0; + Sreg(DS1) = 0; + + CHANGE_PC; +} + + +void nec_common_device::nec_interrupt(unsigned int_num, int/*INTSOURCES*/ source) +{ + UINT32 dest_seg, dest_off; + + i_pushf(); + m_TF = m_IF = 0; + + if (source == INT_IRQ) /* get vector */ + int_num = (standard_irq_callback)(0); + + dest_off = read_mem_word(int_num*4); + dest_seg = read_mem_word(int_num*4+2); + + PUSH(Sreg(PS)); + PUSH(m_ip); + m_ip = (WORD)dest_off; + Sreg(PS) = (WORD)dest_seg; + CHANGE_PC; +} + +void nec_common_device::nec_trap() +{ + (this->*s_nec_instruction[fetchop()])(); + nec_interrupt(NEC_TRAP_VECTOR, BRK); +} + +void nec_common_device::external_int() +{ + if (m_pending_irq & NMI_IRQ) + { + nec_interrupt(NEC_NMI_VECTOR, NMI_IRQ); + m_pending_irq &= ~NMI_IRQ; + } + else if (m_pending_irq) + { + /* the actual vector is retrieved after pushing flags */ + /* and clearing the IF */ + nec_interrupt((UINT32)-1, INT_IRQ); + m_irq_state = CLEAR_LINE; + m_pending_irq &= ~INT_IRQ; + } +} + +/****************************************************************************/ +/* OPCODES */ +/****************************************************************************/ + +#include "necinstr.inc" + +/*****************************************************************************/ + +void nec_common_device::execute_set_input(int irqline, int state) +{ + switch (irqline) + { + case 0: + m_irq_state = state; + if (state == CLEAR_LINE) + m_pending_irq &= ~INT_IRQ; + else + { + m_pending_irq |= INT_IRQ; + m_halted = 0; + } + break; + case INPUT_LINE_NMI: + if (m_nmi_state == state) return; + m_nmi_state = state; + if (state != CLEAR_LINE) + { + m_pending_irq |= NMI_IRQ; + m_halted = 0; + } + break; + case NEC_INPUT_LINE_POLL: + m_poll_state = state; + break; + } +} + +void nec_common_device::device_start() +{ + unsigned int i, j, c; + + static const WREGS wreg_name[8]={ AW, CW, DW, BW, SP, BP, IX, IY }; + static const BREGS breg_name[8]={ AL, CL, DL, BL, AH, CH, DH, BH }; + + for (i = 0; i < 256; i++) + { + for (j = i, c = 0; j > 0; j >>= 1) + if (j & 1) c++; + parity_table[i] = !(c & 1); + } + + for (i = 0; i < 256; i++) + { + Mod_RM.reg.b[i] = breg_name[(i & 0x38) >> 3]; + Mod_RM.reg.w[i] = wreg_name[(i & 0x38) >> 3]; + } + + for (i = 0xc0; i < 0x100; i++) + { + Mod_RM.RM.w[i] = wreg_name[i & 7]; + Mod_RM.RM.b[i] = breg_name[i & 7]; + } + + m_no_interrupt = 0; + m_prefetch_count = 0; + m_prefetch_reset = 0; + m_prefix_base = 0; + m_seg_prefix = 0; + m_EA = 0; + m_EO = 0; + m_E16 = 0; + m_debugger_temp = 0; + m_ip = 0; + + memset(m_regs.w, 0x00, sizeof(m_regs.w)); + memset(m_sregs, 0x00, sizeof(m_sregs)); + + save_item(NAME(m_regs.w)); + save_item(NAME(m_sregs)); + + save_item(NAME(m_ip)); + save_item(NAME(m_TF)); + save_item(NAME(m_IF)); + save_item(NAME(m_DF)); + save_item(NAME(m_MF)); + save_item(NAME(m_SignVal)); + save_item(NAME(m_AuxVal)); + save_item(NAME(m_OverVal)); + save_item(NAME(m_ZeroVal)); + save_item(NAME(m_CarryVal)); + save_item(NAME(m_ParityVal)); + save_item(NAME(m_pending_irq)); + save_item(NAME(m_nmi_state)); + save_item(NAME(m_irq_state)); + save_item(NAME(m_poll_state)); + save_item(NAME(m_no_interrupt)); + save_item(NAME(m_halted)); + save_item(NAME(m_prefetch_count)); + save_item(NAME(m_prefetch_reset)); + + m_program = &space(AS_PROGRAM); + m_direct = &m_program->direct(); + m_io = &space(AS_IO); + + state_add( NEC_PC, "PC", m_debugger_temp).callimport().callexport().formatstr("%05X"); + state_add( NEC_IP, "IP", m_ip).formatstr("%04X"); + state_add( NEC_SP, "SP", Wreg(SP)).formatstr("%04X"); + state_add( NEC_FLAGS, "F", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( NEC_AW, "AW", Wreg(AW)).formatstr("%04X"); + state_add( NEC_CW, "CW", Wreg(CW)).formatstr("%04X"); + state_add( NEC_DW, "DW", Wreg(DW)).formatstr("%04X"); + state_add( NEC_BW, "BW", Wreg(BW)).formatstr("%04X"); + state_add( NEC_BP, "BP", Wreg(BP)).formatstr("%04X"); + state_add( NEC_IX, "IX", Wreg(IX)).formatstr("%04X"); + state_add( NEC_IY, "IY", Wreg(IY)).formatstr("%04X"); + state_add( NEC_ES, "DS1", Sreg(DS1)).formatstr("%04X"); + state_add( NEC_CS, "PS", Sreg(PS)).formatstr("%04X"); + state_add( NEC_SS, "SS", Sreg(SS)).formatstr("%04X"); + state_add( NEC_DS, "DS0", Sreg(DS0)).formatstr("%04X"); + + state_add( STATE_GENPC, "GENPC", m_debugger_temp).callimport().callexport().noshow(); + state_add( STATE_GENSP, "GENSP", m_debugger_temp).callimport().callexport().noshow(); + state_add( STATE_GENFLAGS, "GENFLAGS", m_debugger_temp).formatstr("%16s").noshow(); + + m_icountptr = &m_icount; +} + +void nec_common_device::state_string_export(const device_state_entry &entry, std::string &str) +{ + UINT16 flags = CompressFlags(); + + switch (entry.index()) + { + case STATE_GENFLAGS: + strprintf(str, "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c", + flags & 0x8000 ? 'N':'E', + flags & 0x4000 ? '?':'.', + flags & 0x2000 ? '?':'.', + flags & 0x1000 ? '?':'.', + flags & 0x0800 ? 'O':'.', + flags & 0x0400 ? 'D':'.', + flags & 0x0200 ? 'I':'.', + flags & 0x0100 ? 'T':'.', + flags & 0x0080 ? 'S':'.', + flags & 0x0040 ? 'Z':'.', + flags & 0x0020 ? '?':'.', + flags & 0x0010 ? 'A':'.', + flags & 0x0008 ? '?':'.', + flags & 0x0004 ? 'P':'.', + flags & 0x0002 ? '.':'?', + flags & 0x0001 ? 'C':'.'); + break; + } +} + +void nec_common_device::state_import(const device_state_entry &entry) +{ + switch (entry.index()) + { + case NEC_PC: + if( m_debugger_temp - (Sreg(PS)<<4) < 0x10000 ) + { + m_ip = m_debugger_temp - (Sreg(PS)<<4); + } + else + { + Sreg(PS) = m_debugger_temp >> 4; + m_ip = m_debugger_temp & 0x0000f; + } + break; + + case NEC_FLAGS: + ExpandFlags(m_debugger_temp); + break; + } +} + + +void nec_common_device::state_export(const device_state_entry &entry) +{ + switch (entry.index()) + { + case STATE_GENPC: + case NEC_PC: + m_debugger_temp = (Sreg(PS)<<4) + m_ip; + break; + + case STATE_GENSP: + m_debugger_temp = (Sreg(SS)<<4) + Wreg(SP); + break; + + case NEC_FLAGS: + m_debugger_temp = CompressFlags(); + break; + } +} + + +void nec_common_device::execute_run() +{ + int prev_ICount; + + if (m_halted) + { + m_icount = 0; + debugger_instruction_hook(this, (Sreg(PS)<<4) + m_ip); + return; + } + + while(m_icount>0) { + /* Dispatch IRQ */ + if (m_pending_irq && m_no_interrupt==0) + { + if (m_pending_irq & NMI_IRQ) + external_int(); + else if (m_IF) + external_int(); + } + + /* No interrupt allowed between last instruction and this one */ + if (m_no_interrupt) + m_no_interrupt--; + + debugger_instruction_hook(this, (Sreg(PS)<<4) + m_ip); + prev_ICount = m_icount; + (this->*s_nec_instruction[fetchop()])(); + do_prefetch(prev_ICount); + } +} |