diff options
author | 2015-11-08 12:56:12 +0100 | |
---|---|---|
committer | 2015-11-08 12:56:12 +0100 | |
commit | 7c19aac60e12d6f5ea301bdb34d7826a01e0b06f (patch) | |
tree | f310d86aa2c6bfc19d115307dedde4eb0cd52dad /src/devices/cpu/nec/v25.cpp | |
parent | a57b46ae933badd7441ce1644711dbb851e2b504 (diff) |
Rename *.c -> *.cpp in our source (nw)
Diffstat (limited to 'src/devices/cpu/nec/v25.cpp')
-rw-r--r-- | src/devices/cpu/nec/v25.cpp | 751 |
1 files changed, 751 insertions, 0 deletions
diff --git a/src/devices/cpu/nec/v25.cpp b/src/devices/cpu/nec/v25.cpp new file mode 100644 index 00000000000..384043c8f9c --- /dev/null +++ b/src/devices/cpu/nec/v25.cpp @@ -0,0 +1,751 @@ +// license:BSD-3-Clause +// copyright-holders:Bryan McPhail, Alex W. Jackson +/**************************************************************************** + + NEC V25/V35 emulator + + --------------------------------------------- + + TODO: + + Using V20/V30 cycle counts for now. V25/V35 cycle counts + vary based on whether internal RAM access is enabled (RAMEN). + + BTCLR and STOP instructions not implemented. + + IBRK flag (trap I/O instructions) not implemented. + + Interrupt macro service function not implemented. + + Port implementation is incomplete: mode control registers are ignored. + + Timer implementation is incomplete: polling is not implemented + (reading any of the registers just returns the last value written) + + Serial interface and DMA functions not implemented. + Note that these functions differ considerably between + the V25/35 and the V25+/35+. + + Make internal RAM into a real RAM region, and use an + internal address map (remapped when IDB is written to) + instead of memory access wrapper functions. + That way the internal RAM would be visible to the debugger, + among other benefits. + +****************************************************************************/ + +#include "emu.h" +#include "debugger.h" + +typedef UINT8 BOOLEAN; +typedef UINT8 BYTE; +typedef UINT16 WORD; +typedef UINT32 DWORD; + +#include "v25.h" +#include "v25priv.h" + +const device_type V25 = &device_creator<v25_device>; +const device_type V35 = &device_creator<v35_device>; + + +v25_common_device::v25_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, 17, 0) + , m_fetch_xor(fetch_xor) + , m_PCK(8) + , m_prefetch_size(prefetch_size) + , m_prefetch_cycles(prefetch_cycles) + , m_chip_type(chip_type) + , m_v25v35_decryptiontable(NULL) +{ +} + + +v25_device::v25_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : v25_common_device(mconfig, V25, "V25", tag, owner, clock, "v25", false, 0, 4, 4, V20_TYPE) +{ +} + + +v35_device::v35_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock) + : v25_common_device(mconfig, V35, "V35", tag, owner, clock, "v35", true, BYTE_XOR_LE(0), 6, 2, V30_TYPE) +{ +} + + +TIMER_CALLBACK_MEMBER(v25_common_device::v25_timer_callback) +{ + m_pending_irq |= param; +} + +void v25_common_device::prefetch() +{ + m_prefetch_count--; +} + +void v25_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 v25_common_device::fetch() +{ + prefetch(); + return m_direct->read_byte((Sreg(PS)<<4)+m_ip++, m_fetch_xor); +} + +UINT16 v25_common_device::fetchword() +{ + UINT16 r = FETCH(); + r |= (FETCH()<<8); + return r; +} + +#define nec_common_device v25_common_device + +#include "v25instr.h" +#include "necmacro.h" +#include "necea.h" +#include "necmodrm.h" + +static UINT8 parity_table[256]; + +UINT8 v25_common_device::fetchop() +{ + UINT8 ret; + + prefetch(); + ret = m_direct->read_byte(( Sreg(PS)<<4)+m_ip++, m_fetch_xor); + + if (m_MF == 0) + if (m_v25v35_decryptiontable) + { + ret = m_v25v35_decryptiontable[ret]; + } + return ret; +} + + + +/***************************************************************************/ + +void v25_common_device::device_reset() +{ + attotime time; + + m_ip = 0; + m_IBRK = 1; + m_F0 = 0; + m_F1 = 0; + m_TF = 0; + m_IF = 0; + m_DF = 0; + m_SignVal = 0; + m_AuxVal = 0; + m_OverVal = 0; + m_ZeroVal = 1; + m_CarryVal = 0; + m_ParityVal = 1; + m_pending_irq = 0; + m_unmasked_irq = INT_IRQ | NMI_IRQ; + m_bankswitch_irq = 0; + m_priority_inttu = 7; + m_priority_intd = 7; + m_priority_intp = 7; + m_priority_ints0 = 7; + m_priority_ints1 = 7; + m_IRQS = m_ISPR = 0; + m_nmi_state = 0; + m_irq_state = 0; + m_poll_state = 1; + m_mode_state = m_MF = (m_v25v35_decryptiontable) ? 0 : 1; + m_intp_state[0] = 0; + m_intp_state[1] = 0; + m_intp_state[2] = 0; + m_halted = 0; + + m_TM0 = m_MD0 = m_TM1 = m_MD1 = 0; + m_TMC0 = m_TMC1 = 0; + + m_RAMEN = 1; + m_TB = 20; + m_PCK = 8; + m_IDB = 0xFFE00; + + int tmp = m_PCK << m_TB; + time = attotime::from_hz(unscaled_clock()) * tmp; + m_timers[3]->adjust(time, INTTB, time); + + m_timers[0]->adjust(attotime::never); + m_timers[1]->adjust(attotime::never); + m_timers[2]->adjust(attotime::never); + + SetRB(7); + Sreg(PS) = 0xffff; + Sreg(SS) = 0; + Sreg(DS0) = 0; + Sreg(DS1) = 0; + + CHANGE_PC; +} + + +void v25_common_device::nec_interrupt(unsigned int_num, int /*INTSOURCES*/ source) +{ + UINT32 dest_seg, dest_off; + + i_pushf(); + m_TF = m_IF = 0; + m_MF = m_mode_state; + + switch(source) + { + case BRKN: /* force native mode */ + m_MF = 1; + break; + case BRKS: /* force secure mode */ + if (m_v25v35_decryptiontable) + m_MF = 0; + else + logerror("%06x: BRKS executed with no decryption table\n",PC()); + break; + case INT_IRQ: /* get vector */ + int_num = standard_irq_callback(0); + break; + default: + break; + } + + 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 v25_common_device::nec_bankswitch(unsigned bank_num) +{ + int tmp = CompressFlags(); + + m_TF = m_IF = 0; + m_MF = m_mode_state; + + SetRB(bank_num); + + Wreg(PSW_SAVE) = tmp; + Wreg(PC_SAVE) = m_ip; + m_ip = Wreg(VECTOR_PC); + CHANGE_PC; +} + +void v25_common_device::nec_trap() +{ + (this->*s_nec_instruction[fetchop()])(); + nec_interrupt(NEC_TRAP_VECTOR, BRK); +} + +#define INTERRUPT(source, vector, priority) \ + if(pending & (source)) { \ + m_IRQS = vector; \ + m_ISPR |= (1 << (priority)); \ + m_pending_irq &= ~(source); \ + if(m_bankswitch_irq & (source)) \ + nec_bankswitch(priority); \ + else \ + nec_interrupt(vector, source); \ + break; /* break out of loop */ \ + } + +/* interrupt sources subject to priority control */ +#define SOURCES (INTTU0 | INTTU1 | INTTU2 | INTD0 | INTD1 | INTP0 | INTP1 | INTP2 \ + | INTSER0 | INTSR0 | INTST0 | INTSER1 | INTSR1 | INTST1 | INTTB) + +void v25_common_device::external_int() +{ + int pending = m_pending_irq & m_unmasked_irq; + + if (pending & NMI_IRQ) + { + nec_interrupt(NEC_NMI_VECTOR, NMI_IRQ); + m_pending_irq &= ~NMI_IRQ; + } + else if (pending & SOURCES) + { + for(int i = 0; i < 8; i++) + { + if (m_ISPR & (1 << i)) break; + + if (m_priority_inttu == i) + { + INTERRUPT(INTTU0, NEC_INTTU0_VECTOR, i) + INTERRUPT(INTTU1, NEC_INTTU1_VECTOR, i) + INTERRUPT(INTTU2, NEC_INTTU2_VECTOR, i) + } + + if (m_priority_intd == i) + { + INTERRUPT(INTD0, NEC_INTD0_VECTOR, i) + INTERRUPT(INTD1, NEC_INTD1_VECTOR, i) + } + + if (m_priority_intp == i) + { + INTERRUPT(INTP0, NEC_INTP0_VECTOR, i) + INTERRUPT(INTP1, NEC_INTP1_VECTOR, i) + INTERRUPT(INTP2, NEC_INTP2_VECTOR, i) + } + + if (m_priority_ints0 == i) + { + INTERRUPT(INTSER0, NEC_INTSER0_VECTOR, i) + INTERRUPT(INTSR0, NEC_INTSR0_VECTOR, i) + INTERRUPT(INTST0, NEC_INTST0_VECTOR, i) + } + + if (m_priority_ints1 == i) + { + INTERRUPT(INTSER1, NEC_INTSER1_VECTOR, i) + INTERRUPT(INTSR1, NEC_INTSR1_VECTOR, i) + INTERRUPT(INTST1, NEC_INTST1_VECTOR, i) + } + + if (i == 7) + INTERRUPT(INTTB, NEC_INTTB_VECTOR, 7) + } + } + else if (pending & INT_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" +#include "v25instr.inc" + +/*****************************************************************************/ + +void v25_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_INTP0: + case NEC_INPUT_LINE_INTP1: + case NEC_INPUT_LINE_INTP2: + irqline -= NEC_INPUT_LINE_INTP0; + if (m_intp_state[irqline] == state) return; + m_intp_state[irqline] = state; + if (state != CLEAR_LINE) + m_pending_irq |= (INTP0 << irqline); + break; + case NEC_INPUT_LINE_POLL: + m_poll_state = state; + break; + } +} + +offs_t v25_common_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options) +{ + extern int necv_dasm_one(char *buffer, UINT32 eip, const UINT8 *oprom, const UINT8 *decryption_table); + + return necv_dasm_one(buffer, pc, oprom, m_v25v35_decryptiontable); +} + +void v25_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; + + for (i = 0; i < 4; i++) + m_timers[i] = machine().scheduler().timer_alloc(timer_expired_delegate(FUNC(v25_common_device::v25_timer_callback),this)); + + save_item(NAME(m_ram.w)); + save_item(NAME(m_intp_state)); + + save_item(NAME(m_ip)); + save_item(NAME(m_IBRK)); + save_item(NAME(m_F0)); + save_item(NAME(m_F1)); + save_item(NAME(m_TF)); + save_item(NAME(m_IF)); + save_item(NAME(m_DF)); + save_item(NAME(m_MF)); + save_item(NAME(m_RBW)); + save_item(NAME(m_RBB)); + 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_unmasked_irq)); + save_item(NAME(m_bankswitch_irq)); + save_item(NAME(m_priority_inttu)); + save_item(NAME(m_priority_intd)); + save_item(NAME(m_priority_intp)); + save_item(NAME(m_priority_ints0)); + save_item(NAME(m_priority_ints1)); + save_item(NAME(m_IRQS)); + save_item(NAME(m_ISPR)); + save_item(NAME(m_nmi_state)); + save_item(NAME(m_irq_state)); + save_item(NAME(m_poll_state)); + save_item(NAME(m_mode_state)); + save_item(NAME(m_no_interrupt)); + save_item(NAME(m_halted)); + save_item(NAME(m_TM0)); + save_item(NAME(m_MD0)); + save_item(NAME(m_TM1)); + save_item(NAME(m_MD1)); + save_item(NAME(m_TMC0)); + save_item(NAME(m_TMC1)); + save_item(NAME(m_RAMEN)); + save_item(NAME(m_TB)); + save_item(NAME(m_PCK)); + save_item(NAME(m_IDB)); + 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( V25_PC, "PC", m_debugger_temp).callimport().callexport().formatstr("%05X"); + state_add( V25_IP, "IP", m_ip).formatstr("%04X"); + state_add( V25_SP, "SP", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_FLAGS, "F", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_AW, "AW", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_CW, "CW", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_DW, "DW", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_BW, "BW", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_BP, "BP", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_IX, "IX", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_IY, "IY", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_ES, "DS1", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_CS, "PS", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_SS, "SS", m_debugger_temp).callimport().callexport().formatstr("%04X"); + state_add( V25_DS, "DS0", m_debugger_temp).callimport().callexport().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 v25_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 %d %c%c%c%c%c%c%c%c%c%c%c%c", + flags & 0x8000 ? 'N':'S', + (flags & 0x7000) >> 12, + flags & 0x0800 ? 'O':'.', + flags & 0x0400 ? 'D':'.', + flags & 0x0200 ? 'I':'.', + flags & 0x0100 ? 'T':'.', + flags & 0x0080 ? 'S':'.', + flags & 0x0040 ? 'Z':'.', + flags & 0x0020 ? '1':'.', + flags & 0x0010 ? 'A':'.', + flags & 0x0008 ? '0':'.', + flags & 0x0004 ? 'P':'.', + flags & 0x0002 ? '.':'I', + flags & 0x0001 ? 'C':'.'); + break; + } +} + +void v25_common_device::state_import(const device_state_entry &entry) +{ + switch (entry.index()) + { + case V25_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 V25_SP: + Wreg(SP) = m_debugger_temp; + break; + + case V25_FLAGS: + ExpandFlags(m_debugger_temp); + break; + + case V25_AW: + Wreg(AW) = m_debugger_temp; + break; + + case V25_CW: + Wreg(CW) = m_debugger_temp; + break; + + case V25_DW: + Wreg(DW) = m_debugger_temp; + break; + + case V25_BW: + Wreg(BW) = m_debugger_temp; + break; + + case V25_BP: + Wreg(BP) = m_debugger_temp; + break; + + case V25_IX: + Wreg(IX) = m_debugger_temp; + break; + + case V25_IY: + Wreg(IY) = m_debugger_temp; + break; + + case V25_ES: + Sreg(DS1) = m_debugger_temp; + break; + + case V25_CS: + Sreg(PS) = m_debugger_temp; + break; + + case V25_SS: + Sreg(SS) = m_debugger_temp; + break; + + case V25_DS: + Sreg(DS0) = m_debugger_temp; + break; + } +} + + +void v25_common_device::state_export(const device_state_entry &entry) +{ + switch (entry.index()) + { + case STATE_GENPC: + case V25_PC: + m_debugger_temp = (Sreg(PS)<<4) + m_ip; + break; + + case STATE_GENSP: + m_debugger_temp = (Sreg(SS)<<4) + Wreg(SP); + break; + + case V25_SP: + m_debugger_temp = Wreg(SP); + break; + + case V25_FLAGS: + m_debugger_temp = CompressFlags(); + break; + + case V25_AW: + m_debugger_temp = Wreg(AW); + break; + + case V25_CW: + m_debugger_temp = Wreg(CW); + break; + + case V25_DW: + m_debugger_temp = Wreg(DW); + break; + + case V25_BW: + m_debugger_temp = Wreg(BW); + break; + + case V25_BP: + m_debugger_temp = Wreg(BP); + break; + + case V25_IX: + m_debugger_temp = Wreg(IX); + break; + + case V25_IY: + m_debugger_temp = Wreg(IY); + break; + + case V25_ES: + m_debugger_temp = Sreg(DS1); + break; + + case V25_CS: + m_debugger_temp = Sreg(PS); + break; + + case V25_SS: + m_debugger_temp = Sreg(SS); + break; + + case V25_DS: + m_debugger_temp = Sreg(DS0); + break; + } +} + + +void v25_common_device::execute_run() +{ + int prev_ICount; + + int pending = m_pending_irq & m_unmasked_irq; + + if (m_halted && pending) + { + for(int i = 0; i < 8; i++) + { + if (m_ISPR & (1 << i)) break; + + if (m_priority_inttu == i && (pending & (INTTU0|INTTU1|INTTU2))) + m_halted = 0; + + if (m_priority_intd == i && (pending & (INTD0|INTD1))) + m_halted = 0; + + if (m_priority_intp == i && (pending & (INTP0|INTP1|INTP2))) + m_halted = 0; + + if (m_priority_ints0 == i && (pending & (INTSER0|INTSR0|INTST0))) + m_halted = 0; + + if (m_priority_ints1 == i && (pending & (INTSER1|INTSR1|INTST1))) + m_halted = 0; + + if (i == 7 && (pending & INTTB)) + m_halted = 0; + } + } + + if (m_halted) + { + m_icount = 0; + debugger_instruction_hook(this, (Sreg(PS)<<4) + m_ip); + return; + } + + while(m_icount>0) { + /* Dispatch IRQ */ + if (m_no_interrupt==0 && (m_pending_irq & m_unmasked_irq)) + { + 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); + } +} |