summaryrefslogtreecommitdiffstatshomepage
path: root/src/devices/cpu/m6809/m6809.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/devices/cpu/m6809/m6809.c')
-rw-r--r--src/devices/cpu/m6809/m6809.c608
1 files changed, 608 insertions, 0 deletions
diff --git a/src/devices/cpu/m6809/m6809.c b/src/devices/cpu/m6809/m6809.c
new file mode 100644
index 00000000000..d69b0417453
--- /dev/null
+++ b/src/devices/cpu/m6809/m6809.c
@@ -0,0 +1,608 @@
+// license:BSD-3-Clause
+// copyright-holders:Nathan Woods
+/*** m6809: Portable 6809 emulator ******************************************
+
+ Copyright John Butler
+
+ References:
+
+ 6809 Simulator V09, By L.C. Benschop, Eindhoven The Netherlands.
+
+ m6809: Portable 6809 emulator, DS (6809 code in MAME, derived from
+ the 6809 Simulator V09)
+
+ 6809 Microcomputer Programming & Interfacing with Experiments"
+ by Andrew C. Staugaard, Jr.; Howard W. Sams & Co., Inc.
+
+ System dependencies: UINT16 must be 16 bit unsigned int
+ UINT8 must be 8 bit unsigned int
+ UINT32 must be more than 16 bits
+ arrays up to 65536 bytes must be supported
+ machine must be twos complement
+
+ History:
+
+March 2013 NPW:
+ Rewrite of 6809/6309/Konami CPU; attempted to make cycle exact.
+
+991026 HJB:
+ Fixed missing calls to cpu_changepc() for the TFR and EXG ocpodes.
+ Replaced m6809_slapstic checks by a macro (CHANGE_PC). ESB still
+ needs the tweaks.
+
+991024 HJB:
+ Tried to improve speed: Using bit7 of cycles1/2 as flag for multi
+ byte opcodes is gone, those opcodes now call fetch_effective_address().
+ Got rid of the slow/fast flags for stack (S and U) memory accesses.
+ Minor changes to use 32 bit values as arguments to memory functions
+ and added defines for that purpose (e.g. X = 16bit XD = 32bit).
+
+990312 HJB:
+ Added bugfixes according to Aaron's findings.
+ Reset only sets CC_II and CC_IF, DP to zero and PC from reset vector.
+990311 HJB:
+ Added _info functions. Now uses static m6808_Regs struct instead
+ of single statics. Changed the 16 bit registers to use the generic
+ PAIR union. Registers defined using macros. Split the core into
+ four execution loops for M6802, M6803, M6808 and HD63701.
+ TST, TSTA and TSTB opcodes reset carry flag.
+ Modified the read/write stack handlers to push LSB first then MSB
+ and pull MSB first then LSB.
+
+990228 HJB:
+ Changed the interrupt handling again. Now interrupts are taken
+ either right at the moment the lines are asserted or whenever
+ an interrupt is enabled and the corresponding line is still
+ asserted. That way the pending_interrupts checks are not
+ needed anymore. However, the CWAI and SYNC flags still need
+ some flags, so I changed the name to 'int_state'.
+ This core also has the code for the old interrupt system removed.
+
+990225 HJB:
+ Cleaned up the code here and there, added some comments.
+ Slightly changed the SAR opcodes (similiar to other CPU cores).
+ Added symbolic names for the flag bits.
+ Changed the way CWAI/Interrupt() handle CPU state saving.
+ A new flag M6809_STATE in pending_interrupts is used to determine
+ if a state save is needed on interrupt entry or already done by CWAI.
+ Added M6809_IRQ_LINE and M6809_FIRQ_LINE defines to m6809.h
+ Moved the internal interrupt_pending flags from m6809.h to m6809.c
+ Changed CWAI cycles2[0x3c] to be 2 (plus all or at least 19 if
+ CWAI actually pushes the entire state).
+ Implemented undocumented TFR/EXG for undefined source and mixed 8/16
+ bit transfers (they should transfer/exchange the constant $ff).
+ Removed unused jmp/jsr _slap functions from 6809ops.c,
+ m6809_slapstick check moved into the opcode functions.
+
+*****************************************************************************/
+
+#include "emu.h"
+#include "debugger.h"
+#include "m6809.h"
+#include "m6809inl.h"
+
+
+//**************************************************************************
+// PARAMETERS
+//**************************************************************************
+
+#define LOG_INTERRUPTS 0
+
+// turn off 'unreferenced label' errors
+// this pragma doesn't work on older GCCs, so cut off at 4.2
+#if defined(__GNUC__) && __GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 2)
+#pragma GCC diagnostic ignored "-Wunused-label"
+#endif
+#ifdef _MSC_VER
+#pragma warning( disable : 4102 )
+#endif
+
+//**************************************************************************
+// DEVICE INTERFACE
+//**************************************************************************
+
+const device_type M6809 = &device_creator<m6809_device>;
+const device_type M6809E = &device_creator<m6809e_device>;
+
+
+//-------------------------------------------------
+// m6809_base_device - constructor
+//-------------------------------------------------
+
+m6809_base_device::m6809_base_device(const machine_config &mconfig, const char *name, const char *tag, device_t *owner, UINT32 clock, const device_type type, int divider, const char *shortname, const char *source)
+ : cpu_device(mconfig, type, name, tag, owner, clock, shortname, source),
+ m_lic_func(*this),
+ m_program_config("program", ENDIANNESS_BIG, 8, 16),
+ m_sprogram_config("decrypted_opcodes", ENDIANNESS_BIG, 8, 16),
+ m_clock_divider(divider)
+{
+ m_mintf = NULL;
+}
+
+
+//-------------------------------------------------
+// device_start - device-specific startup
+//-------------------------------------------------
+
+void m6809_base_device::device_start()
+{
+ if (!m_mintf)
+ m_mintf = new mi_default;
+
+ m_mintf->m_program = &space(AS_PROGRAM);
+ m_mintf->m_sprogram = has_space(AS_DECRYPTED_OPCODES) ? &space(AS_DECRYPTED_OPCODES) : m_mintf->m_program;
+
+ m_mintf->m_direct = &m_mintf->m_program->direct();
+ m_mintf->m_sdirect = &m_mintf->m_sprogram->direct();
+
+ m_lic_func.resolve_safe();
+
+ // register our state for the debugger
+ state_add(STATE_GENPC, "GENPC", m_pc.w).noshow();
+ state_add(STATE_GENPCBASE, "GENPCBASE", m_ppc.w).noshow();
+ state_add(STATE_GENFLAGS, "GENFLAGS", m_cc).callimport().callexport().formatstr("%8s").noshow();
+ state_add(M6809_PC, "PC", m_pc.w).mask(0xffff);
+ state_add(M6809_S, "S", m_s.w).mask(0xffff);
+ state_add(M6809_CC, "CC", m_cc).mask(0xff);
+ state_add(M6809_U, "U", m_u.w).mask(0xffff);
+ state_add(M6809_A, "A", m_d.b.h).mask(0xff);
+ state_add(M6809_B, "B", m_d.b.l).mask(0xff);
+ state_add(M6809_X, "X", m_x.w).mask(0xffff);
+ state_add(M6809_Y, "Y", m_y.w).mask(0xffff);
+ state_add(M6809_DP, "DP", m_dp).mask(0xff);
+
+ // initialize variables
+ m_cc = 0;
+ m_pc.w = 0;
+ m_s.w = 0;
+ m_u.w = 0;
+ m_d.w = 0;
+ m_x.w = 0;
+ m_y.w = 0;
+ m_dp = 0;
+ m_reg = 0;
+ m_reg8 = NULL;
+ m_reg16 = NULL;
+
+ // setup regtable
+ save_item(NAME(m_pc.w));
+ save_item(NAME(m_ppc.w));
+ save_item(NAME(m_d.w));
+ save_item(NAME(m_dp));
+ save_item(NAME(m_u.w));
+ save_item(NAME(m_s.w));
+ save_item(NAME(m_x.w));
+ save_item(NAME(m_y.w));
+ save_item(NAME(m_cc));
+ save_item(NAME(m_temp.w));
+ save_item(NAME(m_opcode));
+ save_item(NAME(m_nmi_asserted));
+ save_item(NAME(m_nmi_line));
+ save_item(NAME(m_firq_line));
+ save_item(NAME(m_irq_line));
+ save_item(NAME(m_lds_encountered));
+ save_item(NAME(m_state));
+ save_item(NAME(m_ea.w));
+ save_item(NAME(m_addressing_mode));
+ save_item(NAME(m_reg));
+ save_item(NAME(m_cond));
+
+ // set our instruction counter
+ m_icountptr = &m_icount;
+ m_icount = 0;
+}
+
+
+
+//-------------------------------------------------
+// device_reset - device-specific reset
+//-------------------------------------------------
+
+void m6809_base_device::device_reset()
+{
+ m_nmi_line = false;
+ m_nmi_asserted = false;
+ m_firq_line = false;
+ m_irq_line = false;
+ m_lds_encountered = false;
+
+ m_dp = 0x00; // reset direct page register
+
+ m_cc |= CC_I; // IRQ disabled
+ m_cc |= CC_F; // FIRQ disabled
+
+ m_pc.b.h = m_addrspace[AS_PROGRAM]->read_byte(VECTOR_RESET_FFFE + 0);
+ m_pc.b.l = m_addrspace[AS_PROGRAM]->read_byte(VECTOR_RESET_FFFE + 1);
+
+ // reset sub-instruction state
+ reset_state();
+}
+
+
+
+//-------------------------------------------------
+// device_pre_save - device-specific pre-save
+//-------------------------------------------------
+
+void m6809_base_device::device_pre_save()
+{
+ if (m_reg8 == &m_d.b.h)
+ m_reg = M6809_A;
+ else if (m_reg8 == &m_d.b.l)
+ m_reg = M6809_B;
+ else if (m_reg16 == &m_d)
+ m_reg = M6809_D;
+ else if (m_reg16 == &m_x)
+ m_reg = M6809_X;
+ else if (m_reg16 == &m_y)
+ m_reg = M6809_Y;
+ else if (m_reg16 == &m_u)
+ m_reg = M6809_U;
+ else if (m_reg16 == &m_s)
+ m_reg = M6809_S;
+ else
+ m_reg = 0;
+}
+
+
+//-------------------------------------------------
+// device_post_load - device-specific post-load
+//-------------------------------------------------
+
+void m6809_base_device::device_post_load()
+{
+ m_reg8 = NULL;
+ m_reg16 = NULL;
+
+ switch(m_reg)
+ {
+ case M6809_A:
+ set_regop8(m_d.b.h);
+ break;
+ case M6809_B:
+ set_regop8(m_d.b.l);
+ break;
+ case M6809_D:
+ set_regop16(m_d);
+ break;
+ case M6809_X:
+ set_regop16(m_x);
+ break;
+ case M6809_Y:
+ set_regop16(m_y);
+ break;
+ case M6809_U:
+ set_regop16(m_u);
+ break;
+ case M6809_S:
+ set_regop16(m_s);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// memory_space_config - return the configuration
+// of the specified address space, or NULL if
+// the space doesn't exist
+//-------------------------------------------------
+
+const address_space_config *m6809_base_device::memory_space_config(address_spacenum spacenum) const
+{
+ switch(spacenum)
+ {
+ case AS_PROGRAM: return &m_program_config;
+ case AS_DECRYPTED_OPCODES: return has_configured_map(AS_DECRYPTED_OPCODES) ? &m_sprogram_config : NULL;
+ default: return NULL;
+ }
+}
+
+
+//-------------------------------------------------
+// state_string_export - export state as a string
+// for the debugger
+//-------------------------------------------------
+
+void m6809_base_device::state_string_export(const device_state_entry &entry, std::string &str)
+{
+ switch (entry.index())
+ {
+ case STATE_GENFLAGS:
+ strprintf(str, "%c%c%c%c%c%c%c%c",
+ (m_cc & 0x80) ? 'E' : '.',
+ (m_cc & 0x40) ? 'F' : '.',
+ (m_cc & 0x20) ? 'H' : '.',
+ (m_cc & 0x10) ? 'I' : '.',
+ (m_cc & 0x08) ? 'N' : '.',
+ (m_cc & 0x04) ? 'Z' : '.',
+ (m_cc & 0x02) ? 'V' : '.',
+ (m_cc & 0x01) ? 'C' : '.');
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// disasm_min_opcode_bytes - return the length
+// of the shortest instruction, in bytes
+//-------------------------------------------------
+
+UINT32 m6809_base_device::disasm_min_opcode_bytes() const
+{
+ return 1;
+}
+
+
+//-------------------------------------------------
+// disasm_max_opcode_bytes - return the length
+// of the longest instruction, in bytes
+//-------------------------------------------------
+
+UINT32 m6809_base_device::disasm_max_opcode_bytes() const
+{
+ return 5;
+}
+
+
+//-------------------------------------------------
+// disasm_disassemble - call the disassembly
+// helper function
+//-------------------------------------------------
+
+offs_t m6809_base_device::disasm_disassemble(char *buffer, offs_t pc, const UINT8 *oprom, const UINT8 *opram, UINT32 options)
+{
+ extern CPU_DISASSEMBLE( m6809 );
+ return CPU_DISASSEMBLE_NAME(m6809)(this, buffer, pc, oprom, opram, options);
+}
+
+
+//**************************************************************************
+// CORE EXECUTION LOOP
+//**************************************************************************
+
+//-------------------------------------------------
+// execute_clocks_to_cycles - convert the raw
+// clock into cycles per second
+//-------------------------------------------------
+
+UINT64 m6809_base_device::execute_clocks_to_cycles(UINT64 clocks) const
+{
+ return (clocks + m_clock_divider - 1) / m_clock_divider;
+}
+
+
+//-------------------------------------------------
+// execute_cycles_to_clocks - convert a cycle
+// count back to raw clocks
+//-------------------------------------------------
+
+UINT64 m6809_base_device::execute_cycles_to_clocks(UINT64 cycles) const
+{
+ return cycles * m_clock_divider;
+}
+
+
+//-------------------------------------------------
+// execute_min_cycles - return minimum number of
+// cycles it takes for one instruction to execute
+//-------------------------------------------------
+
+UINT32 m6809_base_device::execute_min_cycles() const
+{
+ return 1;
+}
+
+
+//-------------------------------------------------
+// execute_max_cycles - return maximum number of
+// cycles it takes for one instruction to execute
+//-------------------------------------------------
+
+UINT32 m6809_base_device::execute_max_cycles() const
+{
+ return 19;
+}
+
+
+//-------------------------------------------------
+// execute_input_lines - return the number of
+// input/interrupt lines
+//-------------------------------------------------
+
+UINT32 m6809_base_device::execute_input_lines() const
+{
+ return 3;
+}
+
+
+//-------------------------------------------------
+// execute_set_input - act on a changed input/
+// interrupt line
+//-------------------------------------------------
+
+void m6809_base_device::execute_set_input(int inputnum, int state)
+{
+ if (LOG_INTERRUPTS)
+ logerror("%s: inputnum=%s state=%d totalcycles=%d\n", machine().describe_context(), inputnum_string(inputnum), state, (int) attotime_to_clocks(machine().time()));
+
+ switch(inputnum)
+ {
+ case INPUT_LINE_NMI:
+ // NMI is edge triggered
+ m_nmi_asserted = m_nmi_asserted || ((state != CLEAR_LINE) && !m_nmi_line && m_lds_encountered);
+ m_nmi_line = (state != CLEAR_LINE);
+ break;
+
+ case M6809_FIRQ_LINE:
+ // FIRQ is line triggered
+ m_firq_line = (state != CLEAR_LINE);
+ break;
+
+ case M6809_IRQ_LINE:
+ // IRQ is line triggered
+ m_irq_line = (state != CLEAR_LINE);
+ break;
+ }
+}
+
+
+//-------------------------------------------------
+// inputnum_string
+//-------------------------------------------------
+
+const char *m6809_base_device::inputnum_string(int inputnum)
+{
+ switch(inputnum)
+ {
+ case INPUT_LINE_NMI: return "NMI";
+ case M6809_FIRQ_LINE: return "FIRQ";
+ case M6809_IRQ_LINE: return "IRQ";
+ default: return "???";
+ }
+}
+
+
+//-------------------------------------------------
+// read_exgtfr_register
+//-------------------------------------------------
+
+m6809_base_device::exgtfr_register m6809_base_device::read_exgtfr_register(UINT8 reg)
+{
+ exgtfr_register result;
+ result.byte_value = 0xFF;
+ result.word_value = 0x00FF;
+
+ switch(reg & 0x0F)
+ {
+ case 0: result.word_value = m_d.w; break; // D
+ case 1: result.word_value = m_x.w; break; // X
+ case 2: result.word_value = m_y.w; break; // Y
+ case 3: result.word_value = m_u.w; break; // U
+ case 4: result.word_value = m_s.w; break; // S
+ case 5: result.word_value = m_pc.w; break; // PC
+ case 8: result.byte_value = m_d.b.h; break; // A
+ case 9: result.byte_value = m_d.b.l; break; // B
+ case 10: result.byte_value = m_cc; break; // CC
+ case 11: result.byte_value = m_dp; break; // DP
+ }
+ return result;
+}
+
+
+//-------------------------------------------------
+// write_exgtfr_register
+//-------------------------------------------------
+
+void m6809_base_device::write_exgtfr_register(UINT8 reg, m6809_base_device::exgtfr_register value)
+{
+ switch(reg & 0x0F)
+ {
+ case 0: m_d.w = value.word_value; break; // D
+ case 1: m_x.w = value.word_value; break; // X
+ case 2: m_y.w = value.word_value; break; // Y
+ case 3: m_u.w = value.word_value; break; // U
+ case 4: m_s.w = value.word_value; break; // S
+ case 5: m_pc.w = value.word_value; break; // PC
+ case 8: m_d.b.h = value.byte_value; break; // A
+ case 9: m_d.b.l = value.byte_value; break; // B
+ case 10: m_cc = value.byte_value; break; // CC
+ case 11: m_dp = value.byte_value; break; // DP
+ }
+}
+
+
+//-------------------------------------------------
+// log_illegal - used to log hits to illegal
+// instructions (except for HD6309 which traps)
+//-------------------------------------------------
+
+void m6809_base_device::log_illegal()
+{
+ logerror("%s: illegal opcode at %04x\n", machine().describe_context(), (unsigned) m_pc.w);
+}
+
+
+//-------------------------------------------------
+// execute_one - try to execute a single instruction
+//-------------------------------------------------
+
+void m6809_base_device::execute_one()
+{
+ switch(pop_state())
+ {
+#include "cpu/m6809/m6809.inc"
+ }
+}
+
+
+//-------------------------------------------------
+// execute_run - execute a timeslice's worth of
+// opcodes
+//-------------------------------------------------
+
+void m6809_base_device::execute_run()
+{
+ do
+ {
+ execute_one();
+ } while(m_icount > 0);
+}
+
+
+UINT8 m6809_base_device::mi_default::read(UINT16 adr)
+{
+ return m_program->read_byte(adr);
+}
+
+UINT8 m6809_base_device::mi_default::read_opcode(UINT16 adr)
+{
+ return m_sdirect->read_byte(adr);
+}
+
+UINT8 m6809_base_device::mi_default::read_opcode_arg(UINT16 adr)
+{
+ return m_direct->read_byte(adr);
+}
+
+
+void m6809_base_device::mi_default::write(UINT16 adr, UINT8 val)
+{
+ m_program->write_byte(adr, val);
+}
+
+
+
+//-------------------------------------------------
+// m6809_device
+//-------------------------------------------------
+
+m6809_device::m6809_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : m6809_base_device(mconfig, "M6809", tag, owner, clock, M6809, 1, "m6809", __FILE__)
+{
+}
+
+
+
+//-------------------------------------------------
+// m6809e_device
+//-------------------------------------------------
+
+m6809e_device::m6809e_device(const machine_config &mconfig, const char *tag, device_t *owner, UINT32 clock)
+ : m6809_base_device(mconfig, "M6809E", tag, owner, clock, M6809E, 4, "m6809e", __FILE__)
+{
+}
+
+WRITE_LINE_MEMBER( m6809_base_device::irq_line )
+{
+ set_input_line( M6809_IRQ_LINE, state );
+}
+
+WRITE_LINE_MEMBER( m6809_base_device::firq_line )
+{
+ set_input_line( M6809_FIRQ_LINE, state );
+}
+
+WRITE_LINE_MEMBER( m6809_base_device::nmi_line )
+{
+ set_input_line( INPUT_LINE_NMI, state );
+}